使用ef core项SqlServer插入数据,目前数据量不算大。每秒插入50条,且更新54条记录。一开始需要50ms左右,但是在程序运行7个小时左右速度骤降,需要800ms左右,最长3500ms。
我的实体类结构大致如下:
Deivice{Guid Id, 属性若干}
DeviceStatus{Guid Id, 状态字段若干}
Module1Status{Guid Id, 状态字段若干}
Module2Status{Guid Id, 状态字段若干}
Part{Guid Id, Guid DeviceId, 属性若干} // Device与Part是OneToMany关系
PartStatus{Guid Id, 状态字段若干}
DeviceStatusHistory{Guid Id, 时间, 状态字段若干} // 这里的状态与DeviceStatus中状态对应
PartStatusHistory{Guid Id, 时间, Guid DeviceStatusHistoryId, 状态字段若干} // 这里的状态与PartStatus中状态对应
其中属性在该程序运行时不进行更新(不发生变化,忽略其他应用引起的修改),状态字段时刻发生变化,也就是该程序进行的处理(更新),Device和Part都有一个对应历史状态表,每次状态发生变化后想起插入当前状态数据
插入数据使用了事务进行批量提交,代码片段如下:
1 await using (var trans = await dbContext.Database.BeginTransactionAsync()) 2 { 3 try 4 { 5 #region 映射数据 6 7 var loggedTime = DateTime.Now; 8 9 #endregion 10 11 sw1.Restart(); 12 13 // 添加设备状态日志 14 await dbContext.DeviceStatusHistories.AddAsync(deviceStatusHistory); 15 // 添加部件状态日志 16 await dbContext.PartStatusHistories.AddRangeAsync(partStatusHistories); 17 18 // 更新设备状态 19 await dbContext.DeviceStatus 20 .Upsert(new DeviceStatus() 21 { 22 Id = _session.Device.Id, 23 LoggedTime = loggedTime, 24 //状态若干 25 }).RunAsync(); 26 27 // 更新休眠模块状态 28 await dbContext.Module1Status 29 .Upsert(new Module1Status() 30 { 31 Id = _session.Device.Id, 32 LoggedTime = loggedTime, 33 //状态若干 34 }).RunAsync(); 35 36 // 更新放电模块状态 37 await dbContext.Module2Status 38 .Upsert(new Module2Status() 39 { 40 Id = _session.Device.Id, 41 LoggedTime = loggedTime, 42 //状态若干 43 }).RunAsync(); 44 45 // 更新电池状态 46 await dbContext.PartStatus 47 .UpsertRange(batteries.Select(c => new PartStatus() 48 { 49 Id = c.Id, 50 LoggedTime = loggedTime 51 //状态若干 52 })) 53 .RunAsync(); 54 55 await dbContext.SaveChangesAsync(); 56 await trans.CommitAsync(); 57 } 58 catch (Exception ex) 59 { 60 await trans.RollbackAsync(); 61 logger.LogError(ex.Message, ex); 62 } 63 finally 64 { 65 // GC.Collect(); 66 sw1.Stop(); 67 logger.LogDebug($"SAVE:{sw1.ElapsedMilliseconds}"); 68 } 69 }
以上每个实体都以Id为主键,时间字段建立的索引。
求园内大神指条明路
数据追踪关了试试
推荐博客
https://www.cnblogs.com/zhaow/articles/9335239.html
已经关闭了追踪,使用的这个扩展方法.
.AsNoTracking() // 关闭状态追踪
@Nii Jyeni: 就是还是没效果吗
@winds_随风: 是的
@winds_随风:刚开始都是很快的,然后就一点一点的变慢了
不要一直用一个 dbContext ,数据太多影响效率,可以每一批次的处理使用一个 dbContext,类似于 .net core 的 Scoped
是的,我每一次处理都是使用一个新的dbContext.昨晚连续运行到现在数据胡保存时间又从最开始的40毫秒左右增加到了1000毫秒以上.
@WeihanLi:每一次处理需要新增1+24条数据(两张表),更新1+1+1+24数据(4张表)
@Nii Jyeni: `Upsert`和 `UpsertRange` 是怎么实现的?
@WeihanLi: 使用的开源库(https://github.com/artiomchi/FlexLabs.Upsert),生成upsert或merge into语句
@Nii Jyeni: 你用 `Upsert` 这里只是想部分更新吧,感觉 Upsert 可能会有点影响性能,部分更新可以直接写一个 EF 的扩展方法,更新对象的状态可能会更好一些,`Upsert` 调用 `RunAsync` 的时候就会更新数据库,而 EF 修改数据状态的方式可以延迟更新,最后批量提交到数据库性能会更好一些
@WeihanLi: 感觉还是太慢了,正在尝试Ado.Net,谢谢指导
表里一共多少数据?理论上数据量越来越大,插入速度就会变慢,可以在7个小时后,去掉数据库索引看下时间会不会变快。
其实要是有效率要求的话,批量插入最好使用数据库的批量添加。
最后就是监控一下EF生成的sql语句,自己拿到数据库里去执行一下,看看具体执行计划的耗时,分析到底是数据库原因还是程序原因。
手动置顶,请各位大神和前辈给点指导意见
– Nii Jyeni 4年前