我已经构建了一个多线程程序,它读取一个80 an的XML文件,并使用EF批量扩展将实体插入到Server中。
XML是我希望尽快插入的图表列表。
我需要插入对象作为一个图形来维护它们之间的关系,在这里,我打算运行一系列的清理来删除重复的对象。
我现在的主要问题是
BulkInsert(entities, EncludeGraph = true) 没有我想要的那么快。
这些实体是使用模型首先创建的,在模型中,我从外键约束中禁用了CascadeDelete选项。每个导航属性都会自动生成并创建一个非聚集索引(我认为这是可以的)。
对如何提高插入速度有什么建议吗?
发布于 2021-09-20 11:39:56
在阅读了EFCore.BulkInsert的Sourcecode之后,很明显,它插入整个对象图的方式是首先批量插入所有父节点--然后检索ID,更新第一级的子代父母,然后大容量插入它们,直到最后的叶节点。
这是次优的,因为它需要几次往返来完成一个中等嵌套图。
我能想象的唯一方法是通过手动在SQL之外创建标识值来执行identi插入,然后遍历图并使用其父id更新所有子元素。
然后大容量插入使用标识-插入没有任何往返。
更新:
我把一切都做得很好。插入大约30.000行pr秒(50-60MB/ SQL速度),它在大约45分钟内完成80 in的XML文件。与以前的12+小时过程相比,这是相当可以接受的。
我制作了一系列执行单个任务的线程:
线程1.从压缩的XML存档中读取到ConcurrentQueue
线程2.扫描队列中的开始-停止元素.使用StringBuilder将它们合并到ConcurrentQueue中
线程3.从XML实体中删除命名空间并添加到ConcurrentQueue中
线程4.将XML反序列化为对象并添加到ConcurrentQueue中
线程5.创建1000个对象的列表并添加到ConcurrentQueue中
线程6.使用来自EFCore.BulkExtensions的EFCore.BulkExtensions将对象拆分为实体类型列表,并使用BulkInsert ( KeepIdentity=true )插入DB
var grp = GraphUtil.GetTopologicallySortedGraph(db, list).GroupBy(g => g.Entity.GetType());
foreach (var typecollection in grp)
{
var entityClrType = typecollection.Key;
var sublist = typecollection.Select(s => s.Entity).ToList();
ObjectCount += sublist.Count;
db.BulkInsert(sublist, config, type: entityClrType);
}注意:当反序列化XML的ID时,创建:
private static int _EntityID;
public Entity()
{
EntityID= ++_EntityID;
}注意:我使用的是EFCore Codefirst机制,它自动处理所有导航属性及其关系ID --非常重要!-更新子ID会自动确保父关系。
注意:我正在运行一个反序列化程序(由于静态标识计数器不能运行一个以上的反序列化程序)和6个BulkInserter线程
https://stackoverflow.com/questions/69253140
复制相似问题