服务热线:13616026886

技术文档 欢迎使用技术文档,我们为你提供从新手到专业开发者的所有资源,你也可以通过它日益精进

位置:首页 > 技术文档 > 数据库技术 > Oracle技术 > DBA > 查看文档

oracle中加速数据导入特性和技术研究

  每个数据库管理员都会面临数据导入的问题,这有可能发生在数据库的新老移植过程中,或者是在数据库崩溃后的恢复重建过程中,还有可能是在创建测试数据库的模拟环境过程中,总之作为一名合格的数据库管理员,你应该做好接受各种数据导入请求的技术储备,同时还要尽量满足人本能的对导入速度的苛求。本文仅针对 oracle 数据库所提供的加速数据导入的各种特性和技术进行探讨,其中的一些方法也可以转化应用于其他数据库。以下七种数据导入方法哪个最适用需要针对具体情况具体分析,我也附带列举了影响导入速度的各种因素供斟酌。为了比较各种数据导入方法的效果,我创建了示例表和数据集,并用各种方法导入示例数据集来计算总体导入时间和导入进程占用 cpu 时间,这里得出的时间仅供参考。需要说明的是,建议你使用 oracle 9i 企业版数据库,当然你也可以尝试使用 oracle 7.3 以上的标准版数据库。本文使用的机器配置为:cpu intel p4,内存 256m,数据库 oracle 9i 企业版。

  每个数据库管理员都会面临数据导入的问题,这有可能发生在数据库的新老移植过程中,或者是在数据库崩溃后的恢复重建过程中,还有可能是在创建测试数据库的模拟环境过程中,总之作为一名合格的数据库管理员,你应该做好接受各种数据导入请求的技术储备,同时还要尽量满足人本能的对导入速度的苛求。本文仅针对 oracle 数据库所提供的加速数据导入的各种特性和技术进行探讨,其中的一些方法也可以转化应用于其他数据库。以下七种数据导入方法哪个最适用需要针对具体情况具体分析,我也附带列举了影响导入速度的各种因素供斟酌。为了比较各种数据导入方法的效果,我创建了示例表和数据集,并用各种方法导入示例数据集来计算总体导入时间和导入进程占用 cpu 时间,这里得出的时间仅供参考。需要说明的是,建议你使用 oracle 9i 企业版数据库,当然你也可以尝试使用 oracle 7.3 以上的标准版数据库。本文使用的机器配置为:cpu intel p4,内存 256m,数据库 oracle 9i 企业版。

  示例表结构和数据集

  为了演示和比较各种数据导入方法,我假定数据导入任务是将外部文件数据导入到 oracle 数据库的calls表中,外部数据文件包含十万条呼叫中心记录,将近 6mb 的文件大小,具体的数据示例如下:

82302284384,2003-04-18:13:18:58,5001,投诉,手机三包维修质量
82302284385,2003-04-18:13:18:59,3352,咨询,供水热线的号码
82302284386,2003-04-18:13:19:01,3142,建议,增设公交线路

  接受导入数据的表名是 calls,表结构如下:

name null? type comment
------------ --------- ------------- -----------------
call_id not null number primary key
call_date not null date non-unique index
emp_id not null number
call_type not null varchar2(12)
details varchar2(25)

  逐条数据插入insert

  数据导入的最简单方法就是编写 insert 语句,将数据逐条插入数据库。这种方法只适合导入少量数据,如 sql*plus 脚本创建某个表的种子数据。该方法的最大缺点就是导入速度缓慢,占用了大量的 cpu 处理时间,不适合大批量数据的导入;而其主要优点就是导入构思简单又有修改完善的弹性,不需要多做其它的准备就可以使用。如果你有很多时间没法打发,又想折磨一下数据库和 cpu,那这种方法正适合你。

  为了与其它方法做比较,现将十万条记录通过此方法导入到 calls 表中,总共消耗 172 秒,其中导入进程占用 cpu 时间为 52 秒。

  逐条数据插入 insert,表暂无索引

  为什么上一种方法占用了较多的 cpu 处理时间,关键是 calls 表中已创建了索引,当一条数据插入到表中时,oracle 需要判别新数据与老数据在索引方面是否有冲突,同时要更新表中的所有索引,重复更新索引会消耗一定的时间。因此提高导入速度的好办法就是在创建表时先不创建索引或者在导入数据之前删除所有索引,在外部文件数据逐条插入到表中后再统一创建表的索引。这样导入速度会提高,同时创建的索引也很紧凑而有效,这一原则同样适用于位图索引(bitmap index)。对于主要的和唯一的关键约束(key constraints),可以使之先暂时失效(disabling)或者删除约束来获得同样的效果,当然这些做法会对已经存在的表的外键约束产生相关的影响,在删除前需要通盘斟酌。

  需要说明的是,这种方法在表中已存在很多数据的情况下不太合适。例如表中已有九千万条数据,而此时需要追加插入一千万条数据,实际导入数据节省的时间将会被重新创建一亿条数据的索引所消耗殆尽,这是我们不希望得到的结果。但是,如果要导入数据的表是空的或导入的数据量比已有的数据量要大得多,那么导入数据节省的时间将会少量用于重新创建索引,这时该方法才可以考虑使用。 加快索引创建是另一个需要考虑的问题。为了减少索引创建中排序的工作时间,可以在当前会话中增加 sort_area_size 参数的大小,该参数允许当前会话在内存的索引创建过程中执行更多的排序操作。同样还可以使用 nologging 关键字来减少因创建索引而生成的 redo 日志量,nologging 关键字会对数据库的恢复和 standby 备用数据库产生明显的影响,所以在使用之前要仔细斟酌,到底是速度优先还是稳定优先。

  运用这种方法,先删除 calls 表的主键和不唯一的索引,然后逐条导入数据,完成后重新创建索引( 表在导入数据前是空的)。该方法总共消耗 130 秒,包括重建索引的时间,其中导入进程占用 cpu 时间为 35秒。

  这种方法的优点是可以加快导入的速度并使索引更加紧凑有效;缺点是缺乏通用性,当你对表增加新的复杂的模式元素(索引、外键等)时你需要添加代码、修改导入执行程序。另外针对 7*24 在线要求的数据库在线导入操作时,删除表的索引会对在线用户的查询有很大的性能影响,同时也要考虑,主要或唯一的关键约束条件的删除或失效可能会影响到引用它们的外键的使用。

  批量插入,表暂无索引

  在oracle v6 中 oci 编程接口加入了数组接口特性。数组操作允许导入程序读取外部文件数据并解析后,向数据库提交sql语句,批量插入 sql 语句检索出的数据。oracle 仅需要执行一次 sql 语句,然后在内存中批量解析提供的数据。批量导入操作比逐行插入重复操作更有效率,这是因为只需一次解析 sql 语句,一些数据绑订操作以及程序与数据库之间来回的操作都显著减少,而且数据库对每一条数据的操作都是重复可知的,这给数据库提供了优化执行的可能。其优点是数据导入的总体时间明显减少,特别是进程占用 cpu 的时间。

  需要提醒的是,通过 oci 接口确实可以执行数据批量导入操作,但是许多工具和脚本语言却不支持使用此功能。如果要使用该方法,需要研究你所使用的开发工具是否支持 oci 批量操作功能。导入程序需要进行复杂的编码并可能存在错误的风险,缺乏一定的弹性。

  运用上述方法,程序将外部数据提取到内存中的数组里,并执行批量插入操作(100行/次),保留了表的删除/重建索引操作,总的导入时间下降到 14 秒,而进程占用 cpu 的时间下降到7秒,可见实际导入数据所花费的时间显著下降了 95%。

  create table as select,使用oracle9i的external table

  oracle 9i 的一项新特性就是 external table,它就象通常的数据库表一样,拥有字段和数据类型约束,并且可以查询,但是表中的数据却不存储在数据库中,而是在与数据库相关联的普通外部文件里。当你查询 external table 时,oracle 将解析该文件并返回符合条件的数据,就象该数据存储在数据库表中一样。

  需要注意的是,你可以在查询语句中将 external table 与数据库中其他表进行连接(join),但是不能给 external table 加上索引,并且不能插入/更新/删除数据,毕竟它不是真正的数据库表。另外,如果与数据库相关联的外部文件被改变或者被删除,这会影响到 external table 返回查询结果,所以在变动前要先跟数据库打招呼。

  这种方法为导入数据打开了新的一扇门。你可以很容易的将外部文件与数据库相关联,并且在数据库中创建对应的 external table,然后就可以立即查询数据,就象外部数据已经导入到数据库表中一样。唯一的不足需要明确,数据并未真正导入到数据库中,当外部文件被删除或覆盖时,数据库将不能访问 external table 里的数据,而且索引没有被创建,访问数据速度将有所缓慢。创建 calls_external(external table表)如下,使之与外部数据文件关联:

create table calls_external
(call_id number,
call_date date,
emp_id number,
call_type varchar2(12),
details varchar2(25))
organization external
( type oracle_loader
default directory extract_files_dir
access parameters
(
records delimited by newline
fields terminated by ’,’
missing field values are null
(
call_id, call_date char date_format date mask
"yyyy-mm-dd:hh24:mi:ss",
emp_id, call_type, details
)
)
location (’calls.dat’)
);

  然后将 external table 与真正被使用的表 calls 关联同步,删除 calls 表并重建它:

create table calls
(
call_id number not null,
call_date date not null,
emp_id number not null,
call_type varchar2(12) not null,
details varchar2(25)
)
tablespace tbs1 nologging
as
select call_id, call_date, emp_id, call_type, details
from calls_external;

  因为 calls 表是真正的数据库表,可以创建索引来加快访问,表中的数据将被保留,即使外部数据文件被更新或被删除。在建表语句中nologging关键字用于加快索引重建。

  运用这种方法导入数据,总的导入时间为 15 秒,进程占用 cpu 的时间为8秒,这比前一种方法稍微慢些,但不能就此认为使用 external table 导入数据一定比 oci 批量插入慢。

  这种方法的优点是,未经进行大量的编写代码就取得了不错的结果,不象 oci 批量插入存在编码错误风险,它还可以使用 dbms_job 包调度数据导入进程,实现数据导入的自动化。其缺点是目标表必须先删除后重建,如果只需要导入增量数据时此方法就不合适了,另外用户在表的重建过程中访问数据时会遇到 "table or view does not exist" 的错误,它仅适用于 oracle 9i 以上版本的数据库。

  insert append as select,使用 oracle9i 的 external table

  上一种方法演示了如何创建与外部数据文件关联的数据库表,其表的数据是由外部数据文件映射过来。缺点是数据库表需要被先删除再重建来保持与外部数据文件的一致和同步,对导入增量的数据而不需要删除已有数据的情况不合适。针对这种需求,oracle 提供了 insert 语句外带 append 提示来满足。

insert /*+ append */ into calls
(call_id, call_date, emp_id, call_type, details)
select call_id, call_date, emp_id, call_type, details
from calls_external;

  该语句读取引用外部数据文件的 calls_external 表中内容,并将之增加到表 calls 中。append 提示告诉 oracle 使用快速机制来插入数据,同时可以配合使用表的 nologging 关键字。

  可以预见这种方法与前一方法消耗了相同的时间,毕竟它们是使用 external table 特性导入数据的不同阶段解决方法。如果目标表不是空的,那将会消耗稍微长的时间(因为要重建更长的索引),而前一 create table as select 方法是整体创建索引

  因为 calls 表是真正的数据库表,可以创建索引来加快访问,表中的数据将被保留,即使外部数据文件被更新或被删除。在建表语句中nologging关键字用于加快索引重建。

  运用这种方法导入数据,总的导入时间为 15 秒,进程占用 cpu 的时间为8秒,这比前一种方法稍微慢些,但不能就此认为使用 external table 导入数据一定比 oci 批量插入慢。

  这种方法的优点是,未经进行大量的编写代码就取得了不错的结果,不象 oci 批量插入存在编码错误风险,它还可以使用 dbms_job 包调度数据导入进程,实现数据导入的自动化。其缺点是目标表必须先删除后重建,如果只需要导入增量数据时此方法就不合适了,另外用户在表的重建过程中访问数据时会遇到 "table or view does not exist" 的错误,它仅适用于 oracle 9i 以上版本的数据库。

  insert append as select,使用 oracle9i 的 external table

  上一种方法演示了如何创建与外部数据文件关联的数据库表,其表的数据是由外部数据文件映射过来。缺点是数据库表需要被先删除再重建来保持与外部数据文件的一致和同步,对导入增量的数据而不需要删除已有数据的情况不合适。针对这种需求,oracle 提供了 insert 语句外带 append 提示来满足。

insert /*+ append */ into calls
(call_id, call_date, emp_id, call_type, details)
select call_id, call_date, emp_id, call_type, details
from calls_external;

  该语句读取引用外部数据文件的 calls_external 表中内容,并将之增加到表 calls 中。append 提示告诉 oracle 使用快速机制来插入数据,同时可以配合使用表的 nologging 关键字。

  可以预见这种方法与前一方法消耗了相同的时间,毕竟它们是使用 external table 特性导入数据的不同阶段解决方法。如果目标表不是空的,那将会消耗稍微长的时间(因为要重建更长的索引),而前一 create table as select 方法是整体创建索引。


  

扫描关注微信公众号