前言:刚过完元旦,2019年的第一个任务,是在生产环境将一份配置数据从数据库A迁移到数据B(200多万条记录),数据库B为多个生产系统公用数据库,已效力多年,并且长期处于放养状态,如今早已年弱体衰,反应迟钝。将数据拿到手上后,先弄了1万条记录试下了水,发现不管是通过直接执行INSERT语句还是通过文本导入器,要把这200多万条记录都是我无法承受的痛。无奈之下,只能在网上另寻他法,这才发现原来有此等神器:sqlldr
sqlldr是Oracle内置的工具,用来快速导入大批量数据,直接在命令行调用。
下表是sqlldr的部分参数,其中加粗的为常用参数,标红为必需参数。
注:sqlldr的命令行参数可以直接写在命令行,也可以放在控制文件,也可以直接放在参数文件PARFILE里面(在参数重复指定时,命令行中的参数具有最高的权重)
属性值 | 含义 | 默认值 |
---|---|---|
userid | Oracle的username/password[@servicename] | |
control | 控制文件 | |
log | 日志文件 | 默认与控制文件同名 |
bad | 坏数据文件 | 默认与数据文件同名 |
discard | 丢弃的数据文件 | |
discardmax | 允许丢弃数据的最大值 | 全部 |
skip | 跳过的行/记录数,比如导入的数据文件前几行是表头或者描述 | 0 |
load | 加载的行/记录数 | 全部 |
errors | 允许的错误行/记录数 | 50 |
rows | 每次提交的记录数,该参数受bindsize影响 | 常规路径:64 直接路径:全部 |
bindsize | 每次提交记录的缓冲区的大小(字节) | 256000 |
silent | 禁止输出信息(header,feedback,errors,discards,partitions) | |
parfile | 参数文件:包含参数规范的文件名 | |
direct | 使用直通路径方式导入,不走buffer cache,通过direct path api发送数据到服务器端的加载引擎,加载引擎按照数据块的格式处理数据并直接写向数据文件,因此效率较高 | false |
parallel | 并行导入,仅在直接路径加载时有效,推荐设置true | false |
file | 并行加载时会用到该参数,指定file参数,要加载的内容即只向指定的数据文件写入数据,减少i/o | |
skip_unusable_indexes | 不允许/允许不可用的索引或索引分区 | false |
skip_index_maintenance | 不维护索引,将受影响的索引标记为不可用 | false |
commit_discontinued | 停止加载时提交加载的行 | false |
readsize | 缓冲区大小(字节) | 1048576,最大不超过20m,该参数仅当从数据文件读取时有效,如果是从近制文件读取数据,则默认为64k |
external_table | 使用外部表进行加载 | 不使用 |
generate_only | sqlldr并不执行加载,而是生成创建外部表的sql和处理数据的sql,并保存在log文件中 | |
execute | 执行外部表并加载数据 | |
columnarrayrows | 指定直接路径加载时流缓冲区的行数 | 5000 |
streamsize | 指定直接路径加载时流缓冲区的大小(字节) | 256000 |
multithreading | 指定直接路径加载时是否启用多线程 | |
date_cache | 指定直接路径加载时日期转换用缓存大小(以条目为单位) | 1000 |
no_index_errors | 在任何索引错误上中止加载 | false |
sqlldr导入有两种模式,常规路径加载和直接路径加载,默认使用常规路径加载,当direct=y或者direct=true时,使用直接路径加载。直接路径加载比常规路径拥有更高的效率,但也有诸多限制。
控制文件为sqlldr的必需项,在这个文件里指定了数据源,编码,操作类型,要操作的表等一系列参数,控制文件常见设置如下:
--关闭归档日志,提高导入速度(仅直接路径时有效)
--unrecoverable
load data
--指定编码
characterset 'UTF8'
--1.指定要加载的数据文件
--INFILE 和INDDN是同义词,它们后面都是要加载的数据文件。如果用 * 则表示数据就在控制文件内。
[ { INFILE | INDDN } {file | * } ]
--BADFILE和BADDN是同义词。file指定坏数据保存的文件
[{ BADFILE | BADDN } file ]
--DISCARDFILE和DISCARDDN是同义词。file指定丢弃的数据文件
[{ DISCARDFILE | DISCARDDN } file ]
--2.指定操作类型
--insert:默认值,装载空表,如果原先的表有数据,sqlloader会停止
--append:原先的表有数据 就在表中追加新记录
--replace:删除旧记录(用 delete from table 语句),替换成新装载的记录
--truncate:删除旧记录(用 truncate table 语句),替换成新装载的记录
[ APPEND | REPLACE | INSERT | TRUNCATE ]
--3.指定操作的表
INTO TABLE [user.]table
--4.指定过滤条件
--[when id = id_memo]
--5.指定字段分隔符
--字段分隔符
fields terminated by ','
--字段用什么字符包括起来
optionally enclosed by '"'
--字段没有对应的值时允许为空
trailing nullcols
--6.指定表字段
--常见数据类型
--CHAR 字符
--DATE 日期
--INTEGER 整数
--FLOAT 普通符点
--DOUBLE 双精度符点
(
id,--类型未指定时,默认为character,每个字段的实际解析类型见log文件
code integer,
name char(1000),
sum double,
create_date date "yyyy-mm-dd hh24:mi:ss",
)
编写好控制文件test.ctl后,就可以通过命令行执行导入,由于我使用的数据源文件为PL/SQL直接导出的csv文件,因此要跳过第一行记录(表头),以下为windows下的使用示例:
//常规路径
//调整每次提交的行数和缓冲区大小,以减少事务提交的次数
sqlldr userid=xxx/xxxxxx@xxxxxx control=test.ctl log=test.log skip=1 rows=5000 bindsize=20971520 readsize=20971520
//直接路径
//开启parallel和unrecoverable(控制文件中)
sqlldr userid=xxx/xxxxxx@xxxxxx control=test.ctl log=test.log skip=1 direct=true parallel=true skip_index_maintenance=true readsize=20971520 columnarrayrows=10000 streamsize=20971520 date_cache=5000
执行完后,结果将会保存至test.log文件中(不管成功还是失败),经测试,200万数据,使用常规路径加载在有索引的情况下导入时间约为15分钟左右,使用直接路径加载导入时间约为4分钟左右。
1.数据文件的字段超出最大长度:
检查报错字段长度是否超过255,若超过255,需要在控制文件中指明字段长度,如:char(1000),否则Oracle会默认该字段为VARCHAR(255)/CHAR(255)
2. TERMINATED 和 ENCLOSED 字段后没有终止定界符:
1).检查数据源文件是否格式正确
2).检查报错字段的前一个字段是否为数字,如果该字段为数字,并且在控制文件中指定了数据类型,尝试将指定的数据类型删掉,使用默认的类型,避免转换时匹配不一致
3. ORA-01722: 无效数字:
检查报错字段在控制文件中是否指定了数据类型,尝试将指定的数据类型删掉,使用默认的类型,避免转换时匹配不一致
4.ORA-01438: 值大于为此列指定的允许精度:
检查数据库字段类型与控制文件中指定的字段类型是否一致
5.SQL*Loader-700: 执行重要的分配 [7] 时内存耗尽:
这个异常一般发生在直接路径加载时,如果表字段很多,或者每次向内存加载的行数过大时(columnarrayrows),sql*loader无法向系统申请到足够的内存空间,就会报出out of memory的错误。
检查执行的命令行里是否包含columnarrayrows参数,如果有,将该参数的值调小,如果没有,加入该参数并且设置值小于5000(5000为默认值)
6.sqlldr不是内部命令或外部命令:
sqlldr.exe在Oracle安装路径的BIN文件夹里,先检查是否安装Oracle,如果未安装,则需要安装Oracle;如果已安装,则检查环境变量PATH是否包含Oracle安装路径的BIN文件夹,如果没有,在Path后加上Oracle_Path\BIN
7.SQL*Loader-704: 内部错误: ulconnect: OCIServerAttach [0]
ORA-12154: TNS: 无法解析指定的连接标识符
检查命令行userid参数的值username/password[@servicename]是否正确
8.sqlldr导入中文乱码:
检查Oracle导出服务端与导入服务端字符集是否一致
select * from v$nls_parameters t where t.PARAMETER = 'NLS_CHARACTERSET';
若不一致,则将控制文件里的characterset参数改为与导出服务端字符集一致即可
characterset ‘ZHS16GBK’