SQLite插入很慢?

Ver*_*ion 55 c# sqlite system.data.sqlite

我最近读到了关于SQLite的内容,并认为我会尝试一下.当我插入一条记录时,它表现良好.但是当我插入一百个它需要五秒钟时,随着记录计数的增加,时间也会增加.可能有什么不对?我正在使用SQLite Wrapper (system.data.SQlite):

dbcon = new SQLiteConnection(connectionString);
dbcon.Open();

//---INSIDE LOOP

 SQLiteCommand sqlComm = new SQLiteCommand(sqlQuery, dbcon);

 nRowUpdatedCount = sqlComm.ExecuteNonQuery(); 

//---END LOOP

dbcon.close();
Run Code Online (Sandbox Code Playgroud)

tid*_*all 76

围绕批量插入包装BEGIN\ END语句.Sqlite针对事务进行了优化.

dbcon = new SQLiteConnection(connectionString);
dbcon.Open();

SQLiteCommand sqlComm;
sqlComm = new SQLiteCommand("begin", dbcon);
sqlComm.ExecuteNonQuery(); 
//---INSIDE LOOP

 sqlComm = new SQLiteCommand(sqlQuery, dbcon);

 nRowUpdatedCount = sqlComm.ExecuteNonQuery(); 

//---END LOOP
sqlComm = new SQLiteCommand("end", dbcon);
sqlComm.ExecuteNonQuery(); 
dbcon.close();
Run Code Online (Sandbox Code Playgroud)

  • +1这在[SQLite FAQ,#19](http://www.sqlite.org/faq.html#q19)中提到 - 当你在没有开始/结束时这样做时,SQLite正在创建一个事务每个插入. (9认同)
  • 3`ExecuteNonQuery`,因为`BEGIN`为1,每个`INSERT`为1(或更多),'END`为1.除非您将所有SQL语句添加到一个字符串(由分号分隔),否则您需要多次`ExecuteNonQuery`调用. (3认同)
  • 哇!这造成了巨大的差异。我以每秒 2-3 次的速度执行了大约 65k 次插入。导入我的数据花了很长时间。每 1000 个 INSERT 进行一次事务中断可将速度提高到每秒 1000 个左右。我的导入大约在一分钟内完成。将整个 65k 插入包含在一个大事务中需要几秒钟。提交几乎是瞬时的。令人印象深刻的差异。 (2认同)

dav*_*d_p 32

我到处都读到创建事务是缓慢SQLite写入的解决方案,但重写代码并将所有SQLite写入包装在事务中可能会很长很痛苦.

我找到了一个更简单,安全且非常有效的方法:我启用了一个(默认情况下禁用)SQLite 3.7.0优化:Write-Ahead-Log(WAL).文档说它适用于所有unix(即Linux和OSX)和Windows系统.

怎么样 ?只需在初始化SQLite连接后运行以下命令:

PRAGMA journal_mode = WAL
PRAGMA synchronous = NORMAL
Run Code Online (Sandbox Code Playgroud)

我的代码现在运行速度快〜600%:我的测试套件现在运行38秒而不是4分钟:)

  • 谢谢!顺便说一句,您可能可以使用内存中的sqlite模式进行测试。 (2认同)

Jar*_*ley 31

尝试将所有插入(也称为批量插入)包装到单个事务中:

string insertString = "INSERT INTO [TableName] ([ColumnName]) Values (@value)";

SQLiteCommand command = new SQLiteCommand();
command.Parameters.AddWithValue("@value", value);
command.CommandText = insertString;
command.Connection = dbConnection;
SQLiteTransaction transaction = dbConnection.BeginTransaction();
try
{
    //---INSIDE LOOP
    SQLiteCommand sqlComm = new SQLiteCommand(sqlQuery, dbcon);
    nRowUpdatedCount = sqlComm.ExecuteNonQuery(); 
    //---END LOOP

    transaction.Commit();
    return true;
}
catch (SQLiteException ex)
{
    transaction.Rollback();
}
Run Code Online (Sandbox Code Playgroud)

默认情况下,SQLite会包装事务中的每个插入,这会降低进程的速度:

INSERT非常慢 - 我每秒只能执行几十次INSERT

实际上,SQLite很容易在普通的台式计算机上每秒执行50,000或更多INSERT语句.但它每秒只会进行几十次交易.

事务速度受磁盘驱动器速度的限制,因为(默认情况下)SQLite实际上等待,直到事务完成之前数据确实安全地存储在磁盘表面上.这样,如果您突然断电或者操作系统崩溃,您的数据仍然是安全的.有关详细信息,请阅读SQLite中的原子提交.

默认情况下,每个INSERT语句都是自己的事务.但是如果用BEGIN ... COMMIT包围多个INSERT语句,则所有插入都被分组到一个事务中.提交事务所需的时间在所有随附的insert语句中分摊,因此每个insert语句的时间大大减少.


小智 8

请参阅ADO.NET帮助文件SQLite.NET.chm中的"优化SQL查询".该页面的代码:

using (SQLiteTransaction mytransaction = myconnection.BeginTransaction())
{
  using (SQLiteCommand mycommand = new SQLiteCommand(myconnection))
  {
    SQLiteParameter myparam = new SQLiteParameter();
    int n;

    mycommand.CommandText = "INSERT INTO [MyTable] ([MyId]) VALUES(?)";
    mycommand.Parameters.Add(myparam);

    for (n = 0; n < 100000; n ++)
    {
      myparam.Value = n + 1;
      mycommand.ExecuteNonQuery();
    }
  }
  mytransaction.Commit();
}
Run Code Online (Sandbox Code Playgroud)