OracleBulkCopy 比 Insert 循环快十倍以上,因其绕过 SQL 解析、约束检查(默认)、日志写入(可选)和单行事务开销,直接通过 OCIArrayInsert 走 Oracle Direct Path Load 协议批量写入数据文件格式。
OracleBulkCopy 为什么比 Insert 循环快十倍以上
因为 oraclebulkcopy 绕过了 sql 解析、约束检查(默认)、日志写入(可选)和单行事务开销,直接走 oracle 的 direct path load 协议——本质是调用 oracle 客户端的 ociarrayinsert 接口,把内存数据块按 oracle 数据文件格式“塞”进表里。
但这也意味着它不走常规 DML 流程:触发器不触发、IDENTITY 列不自增、DEFAULT 值不填充、外键和 CHECK 约束默认跳过(除非显式开启验证)。
- 必须用
Oracle.ManagedDataAccess4.122.19.1+(旧版 ODP.NET 非托管驱动不支持OracleBulkCopy) - 目标表不能有启用的触发器(否则抛
ORA-26086: direct path does not support triggers) - 源数据列顺序、类型必须严格匹配目标表(NULL/NOT NULL、精度、字符集都算在内)
如何正确配置 OracleBulkCopy 才不卡死或报 ORA-01652
OracleBulkCopy 默认行为很“省事”,但生产环境极易翻车:它会自动建临时段、分配大量 TEMP 表空间,且批量提交大小(BatchSize)设为 0 时,整个数据集当一个事务处理——千万级数据直接 OOM 或 ORA-01652: unable to extend temp segment。
- 务必设置
BatchSize(推荐 10000–50000),避免单次提交过大;同时设DestinationTableName和ColumnMappings(即使列名一致也建议显式映射,防隐式转换失败) - 关闭约束检查:
bulkCopy.EnableConstraints = false(加载完再手动ALTER TABLE ... ENABLE CONSTRAINTS) - 禁用索引维护:
bulkCopy.FireTriggers = false,否则索引同步会拖慢 3–5 倍 - 连接字符串必须含
Statement Cache Size=0;Connection Timeout=300;,否则连接池可能耗尽
遇到 ORA-01401 / ORA-12899 怎么快速定位字段问题
这类错误不是代码逻辑错,而是源数据超出目标列定义——比如 CHAR(10) 存了 11 字节 UTF-8 中文,或 NUMBER(5,2) 插入了 1234.567。但错误堆栈只报第 1 行,实际可能是第 999999 行出问题。
- 先用
DataTable加载前 1000 行做预检:dt.AsEnumerable().All(r => r["col"].ToString().Length - 对
NUMBER类型,用decimal.TryParse检查精度,别信Convert.ToDecimal的静默截断 - 关键技巧:临时把
OracleBulkCopy.BatchSize = 1000,配合try/catch捕获OracleException,就能准确定位坏数据所在批次 - 字符集注意:
NVARCHAR2要用OracleDbType.NVarchar2映射,否则中文可能被截成乱码再报长度超限
千万级加载后为什么查询反而变慢
数据进去了,但没统计信息、没索引、高水位线(HWM)卡在顶部——这三件事不做,后续查询性能直接打骨折。
- 加载完立刻执行
ANALYZE TABLE xxx COMPUTE STATISTICS(或用DBMS_STATS.GATHER_TABLE_STATS),否则 CBO 用错误基数估算,走错执行计划 - 如果表原来有索引,
OracleBulkCopy不重建它们;得手动ALTER INDEX xxx REBUILD,否则索引碎片率爆表 - 加载后首次
SELECT COUNT(*)可能极慢,因为 Oracle 要扫 HWM 上方所有块;用ALTER TABLE xxx SHRINK SPACE COMPACT收回空闲空间(需开启行移动:ALTER TABLE xxx ENABLE ROW MOVEMENT)
真正难的从来不是“怎么把数据倒进去”,而是“倒完之后,数据库还认得这个表吗”。字段精度、统计信息、索引状态、表空间碎片——漏掉任意一项,极速加载就变成隐形性能陷阱。










