2017-02-03 76 views
5

我的問題直接來自this one,雖然我只對UPDATE感興趣,而且只有這一點。提高SQLite的每秒更新性能?

我已經寫在C/C++一個應用程序,它使用了大量的SQLite,大多SELECT/UPDATE,在一個非常頻繁的間隔(約20個查詢每0.5至1秒)

我的數據庫並不大,約2500個記錄的時刻,這裏是表結構:

CREATE TABLE player (
    id INTEGER PRIMARY KEY AUTOINCREMENT, 
    name VARCHAR(64) UNIQUE, 
    stats VARBINARY, 
    rules VARBINARY 
); 

到現在爲止我也沒用過transactions因爲我提高了代碼,想要刺性能而非表現。

// 10 times execution of this 
UPDATE player SET stats = ? WHERE (name = ?) 

其中stats正好150個字符的JSON和name是從5-10:

然後,我通過僅僅執行10 update查詢,以下的(在不同的值的循環)測量了我的數據庫的性能字符。

沒有交易,其結果是不能接受的: - 約1充滿第二(0.096每個)

隨着交易中,時間下降x7.5時間: - 約0.11 - 0.16秒(0.013每個)

我嘗試刪除大部分數據庫和/或重新排序/刪除列,看看是否改變了什麼,但沒有。即使數據庫僅包含100個記錄(測試),我也會得到上述數字。

然後我試着用PRAGMA選擇玩:

PRAGMA synchronous = NORMAL 
PRAGMA journal_mode = MEMORY 

給我小的時候,但並非總是如此,更像約0.08 - 0.14秒

PRAGMA synchronous = OFF 
PRAGMA journal_mode = MEMORY 

終於給了我非常小的時候大約0.002 - 0.003秒但我不想使用它,因爲我的應用程序每秒都會保存數據庫,並且數據庫損壞的可能性很高OS /電源故障。

C SQLite用於查詢的代碼是:(評論/錯誤處理/不相關的部分省略)

// start transaction 
sqlite3_exec(db, "BEGIN TRANSACTION", NULL, NULL, NULL); 

// query 
sqlite3_stmt *statement = NULL; 
int out = sqlite3_prepare_v2(query.c_str(), -1, &statement, NULL); 
// bindings 
for(size_t x = 0, sz = bindings.size(); x < sz; x++) { 
    out = sqlite3_bind_text(statement, x+1, bindings[x].text_value.c_str(), bindings[x].text_value.size(), SQLITE_TRANSIENT); 
    ... 
} 

// execute 
out = sqlite3_step(statement); 

if (out != SQLITE_OK) { 
    // should finalize the query no mind the error 
    if (statement != NULL) { 
     sqlite3_finalize(statement); 
    } 
} 

// end the transaction 
sqlite3_exec(db, "END TRANSACTION", NULL, NULL, NULL); 

正如你看到的,這是一個非常典型的TABLE,記錄數量少,我做一個簡單的簡單UPDATE正好10次。還有什麼我可以做的,以減少我的UPDATE次?我正在使用最新的SQLite 3.16.2

注:定時以上直接從單個END TRANSACTION查詢到來。查詢完成一個簡單的交易,我使用準備好的語句 。

UPDATE:

我進行了一些測試與交易的啓用和禁用和各種更新計數。我進行以下設置測試:

VACUUM; 
PRAGMA synchronous = NORMAL; -- def: FULL 
PRAGMA journal_mode = WAL; -- def: DELETE 
PRAGMA page_size = 4096;  -- def: 1024 

結果如下:

無交易(10次更新)

  • 0.30800秒(0.0308每次更新)
  • 0.30200秒
  • 0.36200秒
  • 0.28600小號ECS

沒有交易記錄(100次更新)

  • 2.64400秒(0.02644每個更新)
  • 2.61200秒
  • 2.76400秒
  • 2.68700秒

沒有交易附件(1000次更新)

  • 28.02800秒(0.028每個更新)
  • 27.73700秒
  • ...

與交易(10次更新)

  • 0.12800秒(0.0128每個更新)
  • 0.08100秒
  • 0.16400秒
  • 0.10400秒

與交易(100次更新)

  • 0.088秒(0.00088每個更新)
  • 0.091秒
  • 0.052秒
  • 0。101秒

與交易(1000次更新)

  • 0.08900秒(0.000089每個更新)
  • 0.15000秒
  • 0.11000秒
  • 0.09100秒

我的結論是transactionstime cost per query沒有意義。也許時代變得越來越龐大,但我對這些數字不感興趣。在單個交易上,10和1000更新之間幾乎沒有時間成本差異。不過,我想知道這是否是我的機器上的硬件限制,並且不能做太多。看來我不能低於~100毫秒,即使使用WAL,也可以使用單個事務並進行10-1000次更新。

沒有交易,固定的時間成本大約爲0.025秒。

+0

如果使用languiage C/C++,使用正確的標籤。否則,它看起來像C++,而不是**不同的**語言C!這不是一個代碼評論網站。 – Olaf

+0

@Olaf,唯一'C++'的東西是'std :: string';其餘的是'C'。上面我特別強調了這一點。其次,我不希望有人審查我的代碼,我想要一個更好的方法SQLite來解決我的問題 – user6096479

+4

它不會**編譯爲C,因此它不是C.只是因爲你有相同的語法/語法並不意味着你有相同的語義!誰告訴你C++是「帶類的C」是明顯錯誤的,並且不知道它們中的至少一個是否足夠編寫非平凡的代碼。 – Olaf

回答

3

利用這樣小的數據量的,對於數據庫操作本身的時間是微不足道;你測量的是事務開銷(迫使寫入磁盤所需的時間),這取決於操作系統,文件系統和硬件。

如果您可以忍受其限制(主要是無網絡),您可以通過啓用WAL mode來使用異步寫入。

+0

我試着設置'PRAGMA synchronous = NORMAL'和'PRAGMA journal_mode = WAL'來試試'WAL',但我沒有得到任何改進,我的意思是根本沒有。我根本不需要網絡,我想從WAL中受益,我只是沒有獲得任何收益(可能還需要其他一些選項?!?)。另一方面,我只需要每次最多1秒鐘更新最多20個查詢。他們(查詢)可能會更少,但不能更多。在我看來,使用'synchronous = NORMAL或FULL'我不能打破'10 ms /更新查詢'的障礙,除非我選擇對OS進行處理,從而降低安全性。 – user6096479

+0

要檢查WAL是否啓用,請執行'PRAGMA journal_mode;'。 –

+0

我不知道爲什麼,但是我的'PRAGMA journal_mode = WAL'在我的C調用中根本沒有被查詢,所以模式仍然是'DELETE'。當我在'PHPLiteAdmin'上使用查詢時正常執行。然而,我的時間略微減少到大約'0.07 - 0.11',這似乎沒問題。 – user6096479

3

您可能仍然受限於提交事務所花費的時間。在第一個例子中,每筆交易大約需要0.10秒才能完成,這非常接近插入10條記錄的交易時間。如果您在單次交易中批量更新100或1000,會得到什麼樣的結果?另外,SQLite在平均硬盤驅動器上每秒鐘預計大約60次事務,而您只能達到10次左右。磁盤性能可能是這裏的問題嗎?

https://sqlite.org/faq.html#q19

+0

我是否受限於硬盤速度和同步模式等待SQLite驗證數據寫入?因此,如果我選擇安全性而不是性能,那麼對於典型的7200磁盤更新查詢,我僅限於'〜10 ms'?我沒有測試100或1000次更新,因爲我的應用程序每次處理最多隻能處理15-20個查詢(其間有一些SELECT),所以我仿效了這個問題的場景。我一週前拆分了我的磁盤,我會再次回覆並回復:) – user6096479

+0

nope,由10個查詢事務處理,時間從0.10到0.14秒不等。 – user6096479

+0

請參閱我的問題更新 – user6096479

0

嘗試添加索引到數據庫:

CREATE INDEX IDXname ON player (name)