使用Dapper ORM提升SQLite批量插入的性能

12

我正在开发一款桌面应用程序,使用SQLite批量插入成千上万行数据到SQLite数据库中,现在需要优化批量插入的性能。目前在数据库中插入60MB的数据需要长达50秒。

  • 有哪些连接字符串参数可以用来提高性能?是否应该更改缓冲区大小?这是否可以通过连接字符串参数实现?还有其他的连接字符串参数可以提高性能吗?我当前的连接字符串是:

    Data Source=Batch.db;Version=3;Pooling=True;Max Pool Size=10;Synchronous=off;FailIfMissing=True;Journal Mode=Off;

  • 我正在使用Dapper ORM(由StackOverflow的团队构建)。在.NET中,是否有更快的方法可以进行SQLite批量插入?

  • 正在使用System.Data.Sqlite插入SQLite。获取一个特殊编译版本的SQLite以提高性能如何?是否有一个比另一个更好的SQLite版本?目前使用 http://sqlite.phxsoftware.com 的 System.Data.SQLite

  • 目前,我将插入操作包装在事务中以提高速度(这使得性能得到了很大的提升)。

  • 我将数据插入到17个表中的一个表中。是否可以在不同线程上并行化此过程以提高速度?

当前性能。 这是典型的吗?我能做得更好吗?

  • 在具有19个列的表中插入55,000行数据:2.25秒(24k次/秒)
  • 在具有63个列的表中插入10,000行数据:2.74秒(3.7k次/秒)

我喜欢SQLite,但我希望它能更快一些。目前,将我的对象保存到使用XML序列化的XML文件中比保存到SQLite数据库中要快,所以我的老板问:为什么要切换到SQLite?或者我应该使用MongoDB或其他对象数据库吗?


可能是如何提高SQLite的性能?的重复问题...虽然那个问题中编写的代码是C,但在使用System.Data.SQLite的C#编写内容时,我将其作为指南。重要的是正确获取PRAGMA并使用事务。不过,这如何映射到ORM,我不知道。 - Chris J
1
1.73秒内处理了55,000行数据... 很难再更快了 ;) - Felice Pollano
你是否在使用批量接口 cnn.Execute("insert...", new[] {val1, val2, val3}),它能够重复使用命令,因此速度会稍微快一些(或许快5-10%)。 - Sam Saffron
1
@ChrisJ...这是要求对Dapper进行特定的解释,我认为这不是重复问题。 - Sam Saffron
2个回答

17

我终于找到了一个在使用.NET和System.Data.SQLite时高性能批量插入SQLite的技巧。

  • 这个技巧将插入性能提高了4.1倍!
  • 我的总保存时间从27秒缩短到了6.6秒。哇!

本文解释了实现SQLite批量插入的最快方式archive.org链接)。

  • 关键是重复使用相同的参数对象
  • 但对于每个要插入的记录,分配不同的值。

.NET需要构造所有这些DbParameter对象的时间真的很长。例如,对于100k行和30列= 3百万个必须创建的参数对象。相反,只创建和重复使用30个参数对象会更快。

更新新性能:

  • 55,000行(19列)在0.53秒内完成 = 100k次插入/秒
internal const string PeakResultsInsert = @"INSERT INTO PeakResult 
           VALUES(@Id,@PeakID,@QuanPeakID,@ISTDRetentionTimeDiff)";
                    
var command = cnn.CreateCommand();
command.CommandText = BatchConstants.PeakResultsInsert;
        
string[] parameterNames = new[]
{
    "@Id",
    "@PeakID",
    "@QuanPeakID",
    "@ISTDRetentionTimeDiff"
};
        
DbParameter[] parameters = parameterNames.Select(pn =>
{
    DbParameter parameter = command.CreateParameter();
    parameter.ParameterName = pn;
    command.Parameters.Add(parameter);
    return parameter;
}).ToArray();
        
foreach (var peakResult in peakResults)
{
    parameters[0].Value = peakResult.Id;
    parameters[1].Value = peakResult.PeakID;
    parameters[2].Value = peakResult.QuanPeakID;
    parameters[3].Value = peakResult.ISTDRetentionTimeDiff;

    command.ExecuteNonQuery();
}

结果发现对于插入大型表格,我不能使用Dapper。(对于小型表格,我仍然使用Dapper)

注意,我还发现了其他一些事情:

  • 我尝试使用多个线程将数据插入到同一个数据库,但这并没有产生任何改进。(没有任何区别)

  • 从System.Data.Sqlite 1.0.69升级到1.0.79。(在我看来,并没有在性能上产生差异)

  • 我没有给DbParameter分配类型,不管怎样似乎都没有影响性能。

  • 在读取方面,我无法提高Dapper的性能。


@samsaffron,我的性能测试清楚地表明,使用上述代码进行批量插入比将IEnumerable<T>用于Dapper插入快4倍(不幸的是)。我的Dapper代码:(我有什么遗漏吗?)cnn.Execute(BatchConstants.PeakResultsInsert,peakResults,trans); - BrokeMyLegBiking
你能否提供一个链接,包含你的测试工具? - Sam Saffron
我上传了我一直在使用的ConsoleApp。https://docs.google.com/open?id=0B4S2QMZFfsHaYjg0YjRjZmUtNGNkMy00NzA1LTgxNjAtNzJmN2VlOTM1YzAx 这是一个轻量级的测试工具,包括两个方法:InsertPeakResultsDapper()和InsertPeakResultsNativeAdo()。 - BrokeMyLegBiking
你能解释一下这段代码中哪部分阻止它每插入一条记录就运行一次SQL语句吗?这看起来不像是“批量”插入。 - marknuzz
文章链接已经失效,但我会点赞您的解释,即使没有链接,您的答案也很棒。这是值得深思的内容,这个方法也可以适用于其他ORM框架。 - CAD bloke
显示剩余2条评论

0
目前,我正在将插入操作包装在事务中以使其更快(这确实有很大的改进)。在批量插入速度方面,我看到的最大收益是将插入操作分成较小的块。根据平台/模式等不同,块的大小会有所不同。我相信在我的测试中它接近1000左右。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接