提高SQLite编写速度C#

58wvjzkj  于 2023-01-26  发布在  SQLite
关注(0)|答案(4)|浏览(269)

我需要显著提高SQLite的编写速度(或者建议SQLite之外的其他解决方案)。

    • 方案:**
  • 我有71列,每列有365 * 24 * 60个值。(365 =天)
  • 我使用"insert intos"来测试db_performance
  • 为了缩短测试时间,我进行了90天的测试,而不是365天(因此结果时间跨度将为x4)
    • 设置:**我尝试过各种普拉格马
  • 同步关断
  • 锁定模式独占
  • 使用不同的值缓存和页面大小(虽然我读取的值较低可能会提高性能,但对我来说,较高的值做得很好)
  • 日志模式关闭
  • 更改超时值
    • 方法:**
  • #A1收集所有"insert intos",每个都执行非查询,最后执行一个大型事务
  • #A2与上述相同,但具有ParallelForEach和ExecuteNonqueryAsync
  • #A3收集一天内的所有"插入信息",每个信息执行一个事务
    • 表格结构:**
  • #T1包含所有列的一个表
  • #T2每列一个表
    • 结果:**我跑了90天(所以不会花太长时间),主要问题是写作速度。

我测量了5个相位,分别是:

  • #P1设置表格和标题(~8 - 9ms)
  • #P2准备数据(对于执行非查询的每个"插入"命令)(~15000 - 18000ms!)
  • #P3执行事务(约200 - 500 ms)
  • #P4读取一整列(约80 - 200 ms)
  • #P5删除一整列(约1 - 9 ms)

我尝试了前面提到的所有不同的方法和途径,但都无法改进#P2。有什么想法可以解决这个问题吗?或者有什么更好的解决方案,比如无服务器数据库(领域?)吗?
下面是#A1 #P2 #T2的代码,它是目前为止效果最好的...

using (var transaction = sqLiteConnection.BeginTransaction())
{
    using (var command = sqLiteConnection.CreateCommand())
    {
        foreach (var vcommand in values_list)
        {
            command.CommandText = vcommand;
            command.ExecuteNonQuery();
        }
    }
                        
    transaction.Commit();
}

(values list是一个字符串[],其中包含71 * 90个insert intos或Marks版本1中的巨型命令。)
编辑/更新:我尝试了Mark Benningfield的方法,对一个表中所有列的所有值进行一次巨大的"插入",可以将总体速度提高到~8500ms(#P2~7500ms)。
最终更新:好吧,我做了一堆测试,并将总结结果:
为了便于比较,所有数据库都有相同的值,一个值为[129600,71]的二维双精度数组。它们都没有预准备插入语句,因此包括了将值转换为所需格式的生成时间(阶段2)。
SQLite处理一个大型事务需要约14秒(之前的~8s没有生成insert-into-command live)。SQL_CE是此场景的最佳选择。这主要是因为不使用字符串操作("INSERT INTO"),但是使用数据表和行+bulkInsert.Realm是有趣的,特别是对于移动用户-非常直观。但是你不能添加动态对象atm(所以你需要一个静态对象)Influx是另一个很好的时间序列数据库,但是它非常具体,不是嵌入式的,并且有一个糟糕的C#实现(它可能通过控制台执行得更好)。

gk7wooem

gk7wooem1#

您是否尝试过将数据写入文本文件,然后使用import命令(请参阅导入CSV文件)?与INSERT命令不同,这些例程通常忽略触发器并使用直接表访问。

rks48beu

rks48beu2#

让你的插入命令看起来像这样(通过你需要的方式构造它):

INSERT INTO table (col1, col2, col3) VALUES (val1, 'val2', val3),
    (val1, 'val2', val3),
    (val2, 'val2', val3),
    ...
    (val1, 'val2', val3);

然后执行 single insert命令,对已知数据进行批量更新。

gstyhher

gstyhher3#

好吧,我做了一堆测试,并将总结结果:
为了便于比较,所有数据库都有相同的值,一个值为[129600,71]的二维双精度数组。它们都没有预准备插入语句,因此包括了将值转换为所需格式的生成时间(阶段2)。
SQLite处理一个大型事务需要约14秒(之前的~ 8 s没有生成insert-into-command live)。SQL_CE是此场景的最佳选择。这主要是因为不使用字符串操作(“INSERT INTO”),但是使用数据表和行+ bulkInsert. Realm是有趣的,特别是对于移动的用户-非常直观。但是你不能添加动态对象atm(所以你需要一个静态对象)Influx是另一个很好的时间序列数据库,但是它非常具体,不是嵌入式的,并且有一个糟糕的C#实现(它可能通过控制台执行得更好)。

gr8qqesn

gr8qqesn4#

执行批量插入的最快和推荐的方法是使用带参数的预准备语句。这样,语句(命令)只被解析和准备一次,而不必为每行重新解析。SQLite也不必从命令文本解析参数值,而是直接提供和使用它们。对于每行,您只需切换参数。
所以我们不应该这样做:

using (var transaction = sqLiteConnection.BeginTransaction())
{
    using (var command = sqLiteConnection.CreateCommand())
    {
        foreach (var vcommand in values_list)
        {
            command.CommandText = vcommand;
            command.ExecuteNonQuery();
        }
    }
                        
    transaction.Commit();
}

你应该这样做:

using (var transaction = sqLiteConnection.BeginTransaction())
{
    using (var command = sqLiteConnection.CreateCommand())
    {
        // Create command and parameters
        command.CommandText = "INSERT INTO MyTable VALUES (?, ?)";
        var param1 = command.Parameters.Add(null, SqliteType.Integer);
        var param2 = command.Parameters.Add(null, SqliteType.Text);

        foreach (var item in values_list)
        {
            // For each row, only update parameter values
            param1.Value = item.IntProperty;
            param2.Value = item.TextProperty;
            command.ExecuteNonQuery();
        }
    }
                        
    transaction.Commit();
}

这样会执行得更好。语句仅在第一次执行时解析。所有后续执行都将使用已准备好的语句。它还可以保护您免受SQL注入攻击:文本参数不会被插入到实际的SQL语句字符串中,这将允许对语句进行操作。相反,它们将作为值直接传递给SQLite。因此,您不仅可以提高性能,还可以防止最常见的数据库攻击场景之一。
一般规则:永远不要将值直接放入SQL语句中。请始终使用参数。
注:还有其他创建和参数的方法。这只是一个示例。例如,还可以使用命名参数:

// Create command and parameters
    command.CommandText = "INSERT INTO MyTable VALUES (@one, @two)";
    var param1 = command.Parameters.Add("@one", SqliteType.Integer);
    var param2 = command.Parameters.Add("@two", SqliteType.Text);

相关问题