批量插入花费比使用Dapper预期更长的时间
读完这篇文章之后,我决定仔细看看我使用Dapper的方式。
我在一个空的数据库上运行这个代码
var members = new List<Member>(); for (int i = 0; i < 50000; i++) { members.Add(new Member() { Username = i.toString(), IsActive = true }); } using (var scope = new TransactionScope()) { connection.Execute(@" insert Member(Username, IsActive) values(@Username, @IsActive)", members); scope.Complete(); }
花了大约20秒。 这是2500插入/秒。 不错,但不是很好,或者考虑到博客是实现45k插入/秒。 在Dapper中有更有效的方法吗?
此外,作为一个方面说明,通过Visual Studiodebugging器运行此代码需要3分钟! 我觉得debugging器会稍微减慢一点,但我真的很惊讶,看到这么多。
UPDATE
所以这
using (var scope = new TransactionScope()) { connection.Execute(@" insert Member(Username, IsActive) values(@Username, @IsActive)", members); scope.Complete(); }
和这个
connection.Execute(@" insert Member(Username, IsActive) values(@Username, @IsActive)", members);
都花了20秒。
但是这花了4秒钟!
SqlTransaction trans = connection.BeginTransaction(); connection.Execute(@" insert Member(Username, IsActive) values(@Username, @IsActive)", members, transaction: trans); trans.Commit();
使用这种方法,我能够达到的最好效果是在4秒钟内完成5万个logging
SqlTransaction trans = connection.BeginTransaction(); connection.Execute(@" insert Member(Username, IsActive) values(@Username, @IsActive)", members, transaction: trans); trans.Commit();
我最近偶然发现了这个问题,注意到TransactionScope是在连接打开之后创build的(我认为这是因为Dappers Execute不像查询那样打开连接)。 根据这里的答案Q4: https : //stackoverflow.com/a/2886326/455904不会导致连接被TransactionScope处理。 我的同事做了一些快速testing,在TransactionScope之外打开连接大大降低了性能。
所以改为以下应该工作:
// Assuming the connection isn't already open using (var scope = new TransactionScope()) { connection.Open(); connection.Execute(@" insert Member(Username, IsActive) values(@Username, @IsActive)", members); scope.Complete(); }
我发现所有这些例子都不完整。
下面是一些在使用后正确closures连接的代码,并且正确地使用了事务范围来增强Excecute性能,这是基于这个线程中最近更好的答案。
using (var scope = new TransactionScope()) { Connection.Open(); Connection.Execute(sqlQuery, parameters); scope.Complete(); }
对我来说最快的变种:
con.Execute(string.Format(@"insert into Member(Username, IsActive) {0}", string.Join(" union all ", members.Select(x => string.Format("select '{0}','{1}'", x.Username, x.IsActive)))));
这就像生成sql一样:
INSERT TABLENAME (Column1,Column2,...) SELECT 'Column1Row1Value','Column2Row1Value'... UNION ALL SELECT 'Column1Row2value','Column2Row2Value'... UNION ALL SELECT 'Column1Row3value','Column2Row3Value'...
这个查询的工作更快,因为sql增加了一组行,而不是一次添加1行。 瓶颈不是写数据,而是写日志中的内容。
另外,请查看最小化日志logging的规则。