
SQLiteのボトルネックはディスクの遅さではない:ロックとfsyncをPythonで実測する
「ファイルベースだから遅い」は本当か SQLite をサーバー用途に使う話をすると、たいてい「やめておいたほうがいい」と言われます。これ自体はでたらめな脅しではなく、SQLite の公式ドキュメント(Appropriate Uses For SQLite )自身が、同時書き込みの多いサービスにはクライアント/サーバー型のデータベースを勧めています。では、なぜ駄目なのか。理由を考え始めたとき、まず思い浮かぶのはこんな筋書きではないでしょうか。SQLite の実体はただのファイルで、ファイルの読み書きはディスク I/O。Redis のようなキャッシュ(メモリ上でキーと値を預かる、キャッシュ用途の定番サーバーです)が「メモリで持つから速い」と説明されるのなら、その裏返しで、ディスクに読み書きする SQLite は遅いはずだ——一見、筋が通って聞こえます。 本当にそうか、まず手元で測ってみます。10 万行のテーブルに主キーで 1 件ずつ問い合わせる、キャッシュ用途を想定したいちばん素朴な読み取りです。コードは Python の標準ライブラリだけで動きます。実験はすべて使い捨ての Docker コンテナ(Linux)上で実行していて、再現手順は記事末尾にまとめました。 import sqlite3, time, random con = sqlite3.connect("cache.db") con.execute("CREATE TABLE kv (k TEXT PRIMARY KEY, v TEXT)") with con: con.executemany("INSERT INTO kv VALUES (?, ?)", ((f"k{i}", "x" * 100) for i in range(100_000))) keys = [f"k{random.randrange(100_000)}" for _ in range(200_000)] cur = con.cursor() t0 = time.perf_counter() for k in keys: cur.execute("SELECT v FROM kv WHERE k = ?", (k,)) cur.fetchone() print(200_000 / (time.perf_counter() - t0)) 手元(Apple シリコンの Mac 上の Linux コンテナ、SSD)ではこうなりました。 ...