2010-10-03 119 views
51

我最近閱讀了SQLite,並認爲我會試試看。當我插入一條記錄時,它表現良好。但是當我插入一百個時,它需要五秒鐘,並且隨着記錄數量的增加,時間也會增加。什麼可能是錯的?我使用SQLite的包裝(system.data.SQlite):在你的批量插入SQLite插入非常慢?

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

//---INSIDE LOOP 

SQLiteCommand sqlComm = new SQLiteCommand(sqlQuery, dbcon); 

nRowUpdatedCount = sqlComm.ExecuteNonQuery(); 

//---END LOOP 

dbcon.close(); 

回答

68

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(); 
+8

+1這在提到4分鐘[SQLite FAQ,#19](http://www.sqlite.org/faq.html#q19) - 當你沒有開始/結束時,SQLite正在爲每個插入創建一個事務。 – 2010-10-04 00:17:32

+1

爲什麼你使用了3 ExecuteNonQuery,其中一個人可以完成這項工作 – 2010-10-04 00:38:42

+3

3'ExecuteNonQuery'因爲1代表'BEGIN',每個'INSERT'代表1個(或更多),'END'代表1。除非將所有SQL語句添加到一個字符串(用分號分隔),否則您需要多個'ExecuteNonQuery'調用。 – tidwall 2010-10-04 02:02:04

28

嘗試包裝你所有的插件(又名,批量插入)到一個單一的transaction

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(); 
} 

默認情況下,SQLite wraps every inserts in a transaction,這將會減慢的過程:

INSERT真的很慢 - 我每秒只能做幾十個INSERTs

實際上,SQLite在一臺普通臺式機上每秒鐘很容易做到50,000或更多的INSERT語句。但它每秒只能做幾十個事務。

交易速度受磁盤驅動器速度的限制,因爲(默認情況下)SQLite實際上會一直等到數據真正安全存儲在磁盤表面上,然後交易完成。這樣,如果您突然斷電或者您的操作系統崩潰,您的數據仍然安全。有關詳細信息,請閱讀SQLite中的原子提交。

默認情況下,每個INSERT語句都是它自己的事務。但是,如果使用BEGIN ... COMMIT包圍多個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(); 
} 
20

我閱讀無處不在,創造交易是減緩的SQLite寫的解決方案,但它可以是漫長而痛苦的重寫你的代碼和包裝所有的SQLite在交易寫道。

我發現一個更簡單,安全和非常有效的方法:我啓用(默認禁用)SQLite 3.7.0優化:Write-Ahead-Log (WAL)。 該文檔說,它適用於所有unix(即Linux和OSX)和Windows系統。

怎麼樣?只要運行以下命令初始化您的SQLite連接後:

PRAGMA journal_mode = WAL 
PRAGMA synchronous = NORMAL 

我的代碼現在運行〜600%的速度:我的測試套件現在跑38秒而不是:)

+1

謝謝!順便說一句,你可能可以使用內存中的sqlite模式進行測試。 – gavv 2016-04-29 14:40:49

+0

這可能是最好的解決方案,如果您有多個線程保存數據並且不想執行大量代碼更改以將所有插入/更新分組爲1個單一調用 – cahen 2017-03-14 17:24:03