Sqlite3書き込みパフォーマンスの最適化-1秒あたり数百万の書き込み

最近、プロジェクトでSqlite3を使用して結果データ(約100万個のデータ)を保存しました。sqliteデータベースに挿入するのに5分かかり、実際のデータ処理プロセスに時間がかかりすぎて、受け入れられませんでした。どのように?sqliteの書き込みデータのパフォーマンスを最適化する方法は?

最適化手法

情報や他のブロガーのブログをチェックすることで、試してみるポイントがいくつかあることを確認しました。

  • 書き込み同期をオフにします(PRAGMA同期=オフ)。sqlite3には、FULL、NORMAL、OFFの3つの同期モードがあります。システムが予期せず終了すると、セキュリティが徐々に弱まります。FULLモードでは、データが破損し、セキュリティ最高および最低の書き込み速度。OFFモードはFULLモードの50倍以上高速になります。
  • トランザクションを使用して、データベースに挿入する必要のあるデータが多い場合は、それらを1つずつ挿入すると、コミットとディスクIOが頻繁に発生します。トランザクションメカニズムを使用すると、データをバッチで挿入できるため、データを大幅に改善できます。書き込み速度。実際のテスト状況では、トランザクションが開始された後、書き込み速度も50倍近く向上する可能性があります。
  • 実行準備は、SQLステートメントを事前にコンパイルすることと同等であり、SQLステートメントを実行するたびに構文チェックやその他の操作を行う必要がないため、SQLステートメントの実行効率を大幅に最適化できます。原理はLuaJitがLuaを静的マシンに変換するのと少し似ています。コード、実行速度を上げるため。実際の測定状況では、実行準備を使用することで書き込み速度を40倍向上させることができます。
  • メモリモード、sqlite3はメモリモードをサポートし、データベースをメモリに直接作成し、アドレスを開いて「:memory:」を渡します。通常モードと比較して、メモリモードはIO時間を節約できます。メモリモードを使用することの加速のアイデアは次のとおりです。最初にデータベースをメモリに作成し、データが完全に書き込まれた後、「VACUUM INTO'out.db3';」ステートメントを呼び出してディスクに書き込みます。実行準備が有効になっている場合、このメソッドは少し速くなります。少し少し。

効率比較

上記の方法を使用した場合のテスト速度の比較は次のとおりです。

最適化 最適化なし 書き込み同期をオフにします オープントランザクション 実行の準備 メモリモード
1秒あたりの挿入数 13 1321記事 50,000 213万 215万

テストコード

マクロ定義のチェック中にエラーが発生しました:

#define CHECKZERO(a) if((a)!=0) throw("error.");

最適化なし

sqlite3* db = nullptr;
CHECKZERO(sqlite3_open(path, &db));
CHECKZERO(sqlite3_exec(db, "CREATE TABLE Test(ID INTEGER,var0 INTEGER,var1 REAL,var2 TEXT);", 0, 0, 0));
const int maxcount = 100;
for (int i = 0; i < maxcount; i++) {
    CHECKZERO(sqlite3_exec(db, "INSERT INTO Test (ID,var0,var1,var2) VALUES (0,1,2.0,\\"hello sqlite3.\\");", 0, 0, 0));
}
CHECKZERO(sqlite3_close(db));

書き込み同期をオフにします

sqlite3* db = nullptr;
CHECKZERO(sqlite3_open(path, &db));
CHECKZERO(sqlite3_exec(db, "PRAGMA synchronous = OFF", 0, 0, 0));
CHECKZERO(sqlite3_exec(db, "CREATE TABLE Test(ID INTEGER,var0 INTEGER,var1 REAL,var2 TEXT);", 0, 0, 0));
const int maxcount = 10000;
for (int i = 0; i < maxcount; i++) {
    CHECKZERO(sqlite3_exec(db, "INSERT INTO Test (ID,var0,var1,var2) VALUES (0,1,2.0,\\"hello sqlite3.\\");", 0, 0, 0));
}
CHECKZERO(sqlite3_close(db));

オープントランザクション

sqlite3* db = nullptr;
CHECKZERO(sqlite3_open(path, &db));
CHECKZERO(sqlite3_exec(db, "PRAGMA synchronous = OFF", 0, 0, 0));
CHECKZERO(sqlite3_exec(db, "CREATE TABLE Test(ID INTEGER,var0 INTEGER,var1 REAL,var2 TEXT);", 0, 0, 0));
CHECKZERO(sqlite3_exec(db, "BEGIN", 0, 0, 0));
const int maxcount = 1000000;
for (int i = 0; i < maxcount; i++) {
    CHECKZERO(sqlite3_exec(db, "INSERT INTO Test (ID,var0,var1,var2) VALUES (0,1,2.0,\\"hello sqlite3.\\");", 0, 0, 0));
    if (i % 10000 == 9999) {
        CHECKZERO(sqlite3_exec(db, "COMMIT", 0, 0, 0));
        CHECKZERO(sqlite3_exec(db, "BEGIN", 0, 0, 0));
    }
}
CHECKZERO(sqlite3_exec(db, "COMMIT", 0, 0, 0));
CHECKZERO(sqlite3_close(db));

実行の準備

sqlite3* db = nullptr;
CHECKZERO(sqlite3_open(path, &db));
CHECKZERO(sqlite3_exec(db, "PRAGMA synchronous = OFF", 0, 0, 0));
CHECKZERO(sqlite3_exec(db, "CREATE TABLE Test(ID INTEGER,var0 INTEGER,var1 REAL,var2 TEXT);", 0, 0, 0));
// 执行准备
sqlite3_stmt *pPrepare = nullptr;
auto sql = "INSERT INTO Test (ID,var0,var1,var2) VALUES (?,?,?,?);";
CHECKZERO(sqlite3_prepare_v2(db, sql, strlen(sql), &pPrepare, 0));
CHECKZERO(sqlite3_exec(db, "BEGIN", 0, 0, 0));
const int maxcount = 10000000;
for (int i = 0; i < maxcount; i++) {
    CHECKZERO(sqlite3_reset(pPrepare));
    CHECKZERO(sqlite3_bind_int(pPrepare, 1, 0));
    CHECKZERO(sqlite3_bind_int(pPrepare, 2, 1));
    CHECKZERO(sqlite3_bind_double(pPrepare, 3, 2.0));
    const char* str = "hello sqlite3.";
    CHECKZERO(sqlite3_bind_text(pPrepare, 4, str, strlen(str), 0));
    int err = sqlite3_step(pPrepare);
    assert(SQLITE_DONE == err);
    if (i % 10000 == 9999) {
        CHECKZERO(sqlite3_exec(db, "COMMIT", 0, 0, 0));
        CHECKZERO(sqlite3_exec(db, "BEGIN", 0, 0, 0));
    }
}
CHECKZERO(sqlite3_exec(db, "COMMIT", 0, 0, 0));
CHECKZERO(sqlite3_finalize(pPrepare)); // 释放
CHECKZERO(sqlite3_close(db));

メモリモード

sqlite3* db = nullptr;
CHECKZERO(sqlite3_open(":memory:", &db));
CHECKZERO(sqlite3_exec(db, "PRAGMA synchronous = OFF", 0, 0, 0));
CHECKZERO(sqlite3_exec(db, "CREATE TABLE Test(ID INTEGER,var0 INTEGER,var1 REAL,var2 TEXT);", 0, 0, 0));
// 执行准备
sqlite3_stmt *pPrepare = nullptr;
auto sql = "INSERT INTO Test (ID,var0,var1,var2) VALUES (?,?,?,?);";
CHECKZERO(sqlite3_prepare_v2(db, sql, strlen(sql), &pPrepare, 0));
CHECKZERO(sqlite3_exec(db, "BEGIN", 0, 0, 0));
const int maxcount = 10000000;
for (int i = 0; i < maxcount; i++) {
    CHECKZERO(sqlite3_reset(pPrepare));
    CHECKZERO(sqlite3_bind_int(pPrepare, 1, 0));
    CHECKZERO(sqlite3_bind_int(pPrepare, 2, 1));
    CHECKZERO(sqlite3_bind_double(pPrepare, 3, 2.0));
    const char* str = "hello sqlite3.";
    CHECKZERO(sqlite3_bind_text(pPrepare, 4, str, strlen(str), 0));
    int err = sqlite3_step(pPrepare);
    assert(SQLITE_DONE == err);
    if (i % 10000 == 9999) {
        CHECKZERO(sqlite3_exec(db, "COMMIT", 0, 0, 0));
        CHECKZERO(sqlite3_exec(db, "BEGIN", 0, 0, 0));
    }
}
CHECKZERO(sqlite3_exec(db, "COMMIT", 0, 0, 0));
CHECKZERO(sqlite3_finalize(pPrepare)); // 释放
// 导出
CHECKZERO(sqlite3_exec(db, "VACUUM INTO 'out.db3';", 0, 0, 0));
CHECKZERO(sqlite3_close(db));

要約する

このように強力で軽量なデータベースエンジンであるsqlite3は、挿入速度がそれほど遅くなることはありません。使用中に効率の問題が見つかった場合は、適切な使用法が見つからなかったはずです。最終的なテスト結果では、 sqlite31秒あたり驚異的な200万。

完全なテストエンジニアリングコードは、次の場所からダウンロードできます。sqlite3パフォーマンス最適化ソースコードデータ挿入オープントランザクション実行準備パフォーマンス改善1秒あたり数百万のデータ書き込み-その他のドキュメントリソース-CSDNライブラリ

おすすめ

転載: blog.csdn.net/Ango_/article/details/122074816