在我们的开发项目中使用的是EF框架,平时使用一直很方便。直到遇到了一个需要插入大量数据的情况(最大支持30w),然后就发现使用 Add/AddRange -> SaveChanges的方案效率太低了。这种方法,其实在执行插入事件的时候还是一条一条插入的。
然后之前有同事找到了一个扩展插件,Z.EntityFramework.Extensions.EFCore,可以使用其中的BulkSaveChanges把数据作为一个整体代替EF原生态的SaveChanges来保存数据,发现效率确实高了不少。但是由于我们的dotnet版本很低,使用的是插件的最低版本,就发现面临了一个新的问题 -- 每个月初,上一个最低版本就需要升级要给版本,否则授权失败无法使用。
由于已经到线上了,不能保证每个月初都能及时更新版本,由此这个问题交给了我这个小菜鸟来解决。想法是,将数据进行分批处理,想到每次处理的数据有大小限制,因为只有一个功能使用了插件处理,所以没有写复杂的方法来计算每次应该处理多少条数据,大概计算了一下一条数据的上限大小,然后再留有富裕就可以了。
所以大概处理方法是这样的,计算出数据需要分批次处理的次数,然后分批处理,外层嵌套事务,万一出错的话可以及时回滚。
private void batchInsertRecord(List<RecordStruct> recordList)
{
if (recordList.Count <= 0)
return;
int dataCount = recordList.Count();
int times = dataCount / BatchInsertCount + (dataCount % BatchInsertCount == 0 ? 0 : 1);
if (times <= 0)
return;
for (int i = 0; i < times; i++)
{
if (BatchInsertCount * i >= dataCount)
return;
int batchCount = BatchInsertCount;
if (BatchInsertCount * (i + 1) > dataCount)
batchCount = dataCount - BatchInsertCount * i;
List<RecordStruct> insertDatas = recordList.GetRange(BatchInsertCount * i, batchCount);
if (insertDatas.Count <= 0)
continue;
List<string> values = new List<string>();
foreach (var record in insertDatas)
values.Add(string.Format("('{0}', {1}, {2})", record.MainData, record.Param1, record.Param2));
string insertSql = "INSERT INTO `exCodeRecord` (`MainData`, `Param1`,