使用SqlBulkCopy插入100万条MSSQL数据如何优化性能?
首先,针对你遇到的100万行数据插入耗时3分半的问题,先明确:不建议直接用Parallel.For并行执行SqlBulkCopy——数据库写入通常受限于磁盘IO或日志吞吐量,并行插入容易引发连接竞争、锁冲突,反而会拖慢整体速度;而且你当前客户端资源占用低,瓶颈大概率在数据库端或SqlBulkCopy的配置上。
下面是具体的优化措施,按优先级排序:
1. 优化SqlBulkCopy核心配置
你的代码已经用了SqlBulkCopy,但还有关键配置没开启:
- 启用流式传输:默认
EnableStreaming为false,会把整个DataTable加载到内存后再发送给数据库。开启后会以流的方式逐步发送数据,既节省内存,又能降低单次数据传输的开销。 - 调整BatchSize到合适值:10000不是最优解,可以尝试50000、100000(根据数据库性能测试调整),过大的BatchSize可能会导致单次请求超时,过小则会增加数据库交互次数。
修改后的SqlBulkCopy代码示例:
using (var sqlBulk = new SqlBulkCopy(_connectionString)) { sqlBulk.EnableStreaming = true; // 新增:启用流式传输 sqlBulk.BatchSize = 50000; // 调整批次大小,按需测试 sqlBulk.DestinationTableName = "Counterparty"; var dt = DataTableHelpers.ListToDataTable(models); sqlBulk.WriteToServer(dt); }
2. 临时禁用数据库的约束与索引
插入大量数据时,数据库需要维护索引、外键约束、触发器,这会带来极大的性能开销。可以在插入前临时禁用这些对象,完成后再恢复:
注意:操作前确保没有其他业务写入该表,或者在事务中执行,避免数据不一致。
示例SQL(可以在C#中通过SqlCommand执行):
-- 插入前禁用 ALTER TABLE Counterparty DISABLE TRIGGER ALL; ALTER INDEX ALL ON Counterparty DISABLE; -- 插入完成后恢复 ALTER INDEX ALL ON Counterparty REBUILD; ALTER TABLE Counterparty ENABLE TRIGGER ALL;
3. 优化数据库恢复模式
如果你的数据库用的是完整恢复模式,批量插入会生成大量事务日志,拖慢写入速度。可以临时切换为简单恢复模式,插入完成后再切回:
-- 插入前切换 ALTER DATABASE test SET RECOVERY SIMPLE; -- 插入完成后切回(如果需要完整备份) ALTER DATABASE test SET RECOVERY FULL;
提示:切换到简单恢复模式会截断事务日志,所以操作前建议做一次完整数据库备份。
4. 跳过DataTable,直接用IDataReader读取数据
你当前需要把List转成DataTable,这个转换过程本身会占用内存和时间。可以直接用IDataReader代替DataTable,比如用CSV读取工具直接从文件生成IDataReader,跳过List和DataTable的内存开销:
示例(假设用CsvHelper处理CSV文件):
using (var reader = new StreamReader("你的数据文件路径")) using (var csv = new CsvReader(reader, CultureInfo.InvariantCulture)) using (var sqlBulk = new SqlBulkCopy(_connectionString)) { sqlBulk.EnableStreaming = true; sqlBulk.BatchSize = 50000; sqlBulk.DestinationTableName = "Counterparty"; // 映射列(如果CSV列名和表列名一致可省略) sqlBulk.ColumnMappings.Add("Name", "Name"); sqlBulk.ColumnMappings.Add("Comment", "Comment"); sqlBulk.ColumnMappings.Add("Address", "Address"); sqlBulk.ColumnMappings.Add("Phone", "Phone"); sqlBulk.ColumnMappings.Add("IsActive", "IsActive"); sqlBulk.WriteToServer(csv.DataReader); }
这样可以直接从文件流读取数据,不用把100万行加载到内存,既节省资源又提升速度。
5. 检查LocalDB的性能限制
你用的是(localdb)\\MSSQLLocalDB,这是轻量级的SQL Server实例,默认配置可能限制了内存和CPU使用。可以通过SSMS连接到LocalDB,查看数据库属性中的“内存”配置,适当调高最大内存分配;另外,LocalDB的磁盘IO性能也不如正式的SQL Server实例,如果数据量长期很大,建议迁移到正式的SQL Server。
内容的提问来源于stack exchange,提问作者 cickness

