SQLite の INSERT は遅いのか?
MySQLとSQLite(PrinCo.)という記事経由で、生まれ変わるPHP - Zend Engine 2、SQLiteの実力は?(MYCOM PC WEB)という記事を読んだ。
MySQL と SQLite で INSERT 時の処理速度が公開されている。 1000件のレコードのINSERTを実行し、それぞれにどれぐらいの時間がかかっているかを示しているのだ。
MySQLが 0.4秒、SQLite が 16秒だというのだが、これはあまりにもひどすぎる。しかも、結論としてどうやら大量データの連続挿入はSQLiteの不得意な処理のようだ。
と書かれている。1000件程度でこんなにかかるんだったら、10000件だったら160秒ぐらいかかる(単純すぎ。)って事?
実はこれにはからくりがあって、SQLite の処理は トランザクション の中でないと著しく遅いのだ。おそらく、これが原因なのではないかと思っている。
上記記事のベンチマーク結果を覆す事は出来ない(PHP5 と MySQL が、私の環境では使えていない)のだが、トランザクションを使った場合と、使わなかった場合の処理速度の違いを示す事は出来る。C言語で同じような処理を行うプログラムを作って、その処理時間を調べてみた。まずは、トランザクションを使った場合。
% /usr/bin/time -h ./instest
0.41s real 0.06s user 0.00s sys
すばらしい処理時間(0.4秒)だ。
次に使わなかった場合。
% /usr/bin/time -h ./instest
3m19.09s real 0.43s user 1.25s sys
私のプログラムがヘボイのか、記事の結果とあまりにも違いすぎる(3分ってどういう事?)のが気になる。しかし、ここでわかる事は SQLite での処理は、トランザクションが必要だろうという事だ。これは、SQLiteが、複数のプロセスからのアクセスがあっても データベースファイルが壊れないように、1件づつの処理で色々とやっているからだろう。以前調べた事だが、トランザクション中は、別のプロセスがファイルにアクセスしようとしてもロックされた状態になる。トランザクション中は書き放題なので速いのだろう。
最後に、今回の実験に利用したプログラムのソースコードを載せておく。
#include <stdio.h>
#include <sqlite.h>
#define INSERT_COUNT 1000
int main(int argc, char *argv[])
{
sqlite *db;
char *errmsg;
int i;
/* SQLite データベースを OPEN */
db = sqlite_open("instest.db", 0755, &errmsg);
if (!db) {
printf("%s: %s\n", argv[0], errmsg);
exit(-1);
}
/* BEGIN TRANSACTION */
#ifdef USE_TRANSACTION
(void)sqlite_exec(db, "BEGIN", NULL, NULL, &errmsg);
#endif
/* INSERT */
for (i = 0; i < INSERT_COUNT; i++) {
char *sql;
sql = sqlite_mprintf("INSERT INTO test VALUES (%d)", i);
(void)sqlite_exec(db, sql, NULL, NULL, &errmsg);
}
/* COMMIT TRANSACTION */
#ifdef USE_TRANSACTION
(void)sqlite_exec(db, "COMMIT", NULL, NULL, &errmsg);
#endif
sqlite_close(db);
}
