使用Dapper执行Oracle批量插入速度过慢的优化方案咨询
优化Oracle批量插入性能的几种实用方案
哇,200条数据插入耗时10秒确实有点拉胯,咱们来一步步把这个性能提上去!先拆解下你当前代码的核心问题:你用Dapper的ExecuteAsync传入集合时,默认是逐条执行每条插入语句——哪怕在事务里,每条都要走一次网络请求,200条就是200次往返,这才是慢的根源。下面给你几个针对性的优化方案,按性能提升幅度排序:
1. 用OracleBulkCopy(性能天花板)
Oracle官方提供的批量加载工具,直接绕过常规SQL解析,和数据库底层批量接口交互,是大数据量插入的最优解,200条数据用这个基本能降到毫秒级。
public async Task BulkInsertAsync() { var items = GetItems().ToList(); using var connection = OpenOracleConnection() as OracleConnection; using var transaction = connection.BeginTransaction(); // 把数据转换成DataTable(OracleBulkCopy需要的格式) var dataTable = new DataTable(); dataTable.Columns.Add("Column1", typeof(int)); dataTable.Columns.Add("Column2", typeof(int)); dataTable.Columns.Add("Column3", typeof(string)); dataTable.Columns.Add("Column4", typeof(string)); dataTable.Columns.Add("Column5", typeof(string)); dataTable.Columns.Add("Column6", typeof(string)); dataTable.Columns.Add("Column7", typeof(string)); dataTable.Columns.Add("Column8", typeof(DateTime)); dataTable.Columns.Add("Column9", typeof(string)); dataTable.Columns.Add("Column10", typeof(int)); foreach (var item in items) { dynamic dynItem = item; dataTable.Rows.Add(dynItem.p1, dynItem.p2, dynItem.p3, dynItem.p4, dynItem.p5, dynItem.p6, dynItem.p7, dynItem.p8, dynItem.p9, dynItem.p10); } // 执行批量插入 using var bulkCopy = new OracleBulkCopy(connection, OracleBulkCopyOptions.UseInternalTransaction, transaction); bulkCopy.DestinationTableName = "TableName"; // 显式映射列(列名一致时可省略,显式更稳妥) bulkCopy.ColumnMappings.Add("Column1", "Column1"); bulkCopy.ColumnMappings.Add("Column2", "Column2"); bulkCopy.ColumnMappings.Add("Column3", "Column3"); bulkCopy.ColumnMappings.Add("Column4", "Column4"); bulkCopy.ColumnMappings.Add("Column5", "Column5"); bulkCopy.ColumnMappings.Add("Column6", "Column6"); bulkCopy.ColumnMappings.Add("Column7", "Column7"); bulkCopy.ColumnMappings.Add("Column8", "Column8"); bulkCopy.ColumnMappings.Add("Column9", "Column9"); bulkCopy.ColumnMappings.Add("Column10", "Column10"); await bulkCopy.WriteToServerAsync(dataTable); transaction.Commit(); }
2. Dapper+Oracle FORALL(改动最小的优化)
如果不想换批量工具,用Oracle原生的FORALL语句结合Dapper的数组参数,把所有数据打包成数组一次性发送到数据库,减少网络往返次数。
public async Task InsertAsync() { var items = GetItems().ToList(); using var connection = OpenOracleConnection(); using var transaction = connection.BeginTransaction(); // 把每个字段提取成数组 var p1 = items.Select(x => ((dynamic)x).p1).ToArray(); var p2 = items.Select(x => ((dynamic)x).p2).ToArray(); var p3 = items.Select(x => ((dynamic)x).p3).ToArray(); var p4 = items.Select(x => ((dynamic)x).p4).ToArray(); var p5 = items.Select(x => ((dynamic)x).p5).ToArray(); var p6 = items.Select(x => ((dynamic)x).p6).ToArray(); var p7 = items.Select(x => ((dynamic)x).p7).ToArray(); var p8 = items.Select(x => ((dynamic)x).p8).ToArray(); var p9 = items.Select(x => ((dynamic)x).p9).ToArray(); var p10 = items.Select(x => ((dynamic)x).p10).ToArray(); // 用Oracle FORALL批量执行插入 await connection.ExecuteAsync(@" FORALL i IN 1..:p1.Count INSERT INTO TableName (Column1, Column2, Column3, Column4, Column5, Column6, Column7, Column8, Column9, Column10) VALUES (:p1(i), :p2(i), :p3(i), :p4(i), :p5(i), :p6(i), :p7(i), :p8(i), :p9(i), :p10(i)) ", new { p1, p2, p3, p4, p5, p6, p7, p8, p9, p10 }, transaction: transaction); transaction.Commit(); }
3. 额外的小优化(锦上添花)
- 修复Random的小问题:你当前
GetItems里每次循环都new一个Random,会因为系统时钟种子重复导致随机数重复,改成只实例化一次:private static IEnumerable<object> GetItems() { var list = new List<object>(); var random = new Random(); // 只创建一次 for (int i = 0; i < 200; i++) { list.Add(new { p1 = random.Next(0,999999), p2 = 1, p3 = "-", p4 = "--", p5 = "--", p6 = "---", p7 = "----", p8 = DateTime.Now, p9 = "-", p10 = 1 }); } return list; } - 临时禁用索引/约束:如果表上有大量索引或外键,插入时可以临时禁用,插入完成后再重建(仅适合一次性批量插入,并发场景别用):
-- 禁用索引 ALTER INDEX idx_tablename_column1 DISABLE; -- 插入完成后重建 ALTER INDEX idx_tablename_column1 REBUILD; - 优化连接池:确保连接字符串开启连接池(默认
Pooling=true),可以设置Min Pool Size避免频繁创建销毁连接的开销。
内容的提问来源于stack exchange,提问作者Fatih Yıldırım




