世界を動かす技術を、日本語で。

SQLiteには(Rustスタイルの)エディションが必要です

2026年7月16日原文(mort.coffee)

概要

SQLite は多くの組み込みプロジェクトやサーバーソフトウェアで利用される、非常に優れた ローカルデータベースエンジン。 しかし、 デフォルト設定 には大きな問題点が存在し、データ整合性やパフォーマンス、型安全性に悪影響を及ぼす。 主な問題点は、 外部キー制約の無効化型の緩さ同時書き込み時のエラーパフォーマンス設定の不備。 それぞれの問題点と、その 対策方法 を具体的に解説。 今後の改善策として「 エディション制」の導入も提案。

SQLiteの優位性と特徴

  • SQLite は、 自己完結型 のライブラリとして動作するRDBMS
  • 組み込み用途 や一部サーバー用途(例:lobste.rs)で業界標準
  • プロセス分離不要、ソフトウェア単体でデータベース機能を実現
  • シリアライザーやパーサー不要、ファイルフォーマットとRDBMSの利点を両立
  • 手軽さと柔軟性 が魅力

悪いデフォルト設定 #1:外部キー制約が無効

  • 外部キー制約 はデータベースの 整合性維持 に不可欠
    • 例:posts.user_idがusers.idを参照
  • SQLiteはデフォルトで外部キー制約を無視
  • ROWID再利用 により、誤った参照が発生しやすい
    • 例:削除→再挿入でIDが再利用され、データが誤って紐付く
  • 解決策PRAGMA foreign_keys = ON;で明示的に有効化
  • 他のRDBMS では標準で有効なため、SQLiteの仕様は特異

悪いデフォルト設定 #2:カラム型の緩さ

  • SQLiteの型システム は「型アフィニティ」に基づく
  • INTEGERカラム でも 文字列やBLOB が格納可能
    • 例:duration_secに「Way too long, I mean come on」も格納可
  • 型エラーが発生しない ため、バグの温床
  • strictテーブル を使えば型エラーを強制できる
    • 例:CREATE TABLE ... strict;
  • strict指定はテーブルごと で、全体適用は不可
  • 柔軟な型名利用独自型エイリアス の文化も存在
    • SQL標準のCREATE DOMAIN(型エイリアス+制約)未対応

悪いデフォルト設定 #3:同時書き込み時のSQLITE_BUSYエラー

  • SQLiteは同時リーダー可、同時ライター不可
  • 書き込み競合時、即座にSQLITE_BUSYエラー発生
  • 期待される挙動 :ロック解除まで一定時間待機
  • 解決策PRAGMA busy_timeout = 5000;でタイムアウト設定
  • 現状のデフォルト では、管理作業やCLI操作時にクラッシュの危険

悪いデフォルト設定 #4:パフォーマンス

  • SQLiteのパフォーマンス は設定次第で大きく向上
  • Write-Ahead Log(WAL) がデフォルトで無効
    • 有効化:PRAGMA journal_mode = WAL;
  • WAL有効化 で書き込み速度が大幅向上
  • 同期設定PRAGMA synchronous = NORMAL;で最適化可能
  • 適切なチューニング でPostgreSQLやMySQL並みの性能も実現可能

デフォルト問題の根本要因と今後の提案

  • 後方互換性 維持のため、現状のデフォルトが継続
    • 既存ソフトの壊滅的影響を回避
  • エディション制導入 の提案
    • 新規プロジェクト向けに「推奨設定エディション」を作成
    • 既存プロジェクトは従来設定のまま利用可
  • SQL標準準拠の型エイリアス(CREATE DOMAIN)カスタム制約 の実装も期待

まとめ

  • SQLite は非常に優れた ローカルデータベース だが、 デフォルト設定 には注意が必要
  • 外部キー制約・型安全・同時書き込み・パフォーマンス の各項目で 明示的な設定 が推奨
  • 今後の発展 には、 エディション制SQL標準準拠 への対応が鍵

Hackerたちの意見

こんにちは、作者です。これがHNに載るのを見るのは楽しいですね。

提案:これをhttps://sqlite.org/forum/forumに投稿してみては?SQLiteチームはそのフォーラムをよく監視していて、過去に質問や提案に対して素晴らしい回答をもらったことがあります。

ちょっとした間違いを見つけたかも。修正した方がいいかもね。「これにより、ダングリング参照が簡単に間違ったカラムへの参照になる」という部分は、「...間違った行への参照になる」って書いた方が良さそう。 (SQLiteのROWIDを再利用する傾向についての段落でね。)

SQLiteはRustとは少し違って、データコンテナなんですよね。SQLiteのデータベースファイルを一台のマシンから別のマシンに移動させて、コマンドラインツールで確認するのが一般的です。アプリに埋め込まれているSQLiteのバージョンが、/usr/bin/sqlite3のバージョンよりも新しいことが多いです。SQLiteファイルにエディションを追加すると、古いバージョンで新しいバージョンが書いたデータベースを読むという使い方が壊れるかもしれません。新しいエディションで何が変わったのか分からないからです。でも、大した問題じゃないと思います。アプリで使っているのと同じバージョンのコマンドラインユーティリティをバンドルするための、より良い操作が必要なだけかもしれません。

これらのプラグマのいくつかは、「エディション」があってもなくても同じ問題がありますよね?ビジータイムアウトは接続ごとですし、WALモードで実行している場合は、ユーザーがそれを知っておかなければ、.dbファイルだけをコピーしてデータベースを壊すリスクがあります。

SQLiteは、データファイルに完全にバンドルすることでこの問題を解決できるかもしれませんね。結局、バイナリは1MB未満ですし、データベースを置く場所にはそれ以上のオーバーヘッドがあるはずです。ほとんどのアプリケーションにとっては、ほんの少しの追加に過ぎません。このアプローチを取るデータベース実装があるのか、またはこれがうまくいかない理由を知りたいです。

エディションは特定の意味的変更について自己記述的であり、ファイルに埋め込むことができるかもしれません。古いバージョンは安全に無視できて、新しいバージョンはそれを解析して実行できます。また、「エディションは早めに宣言しなければならない」「エディションは一方向のみ」などの制約を強制することで、変更の採用に一定のセキュリティレベルを持たせることもできるでしょう。

これらの変更を加えても、完全に後方互換性を保てると思うよ。SQLiteには、最後に書き込んでからデータベースが変更されてないか確認するためのメタデータがたくさんあるし。新しいエディションには厳しいルールを守らせるけど、古いエディションがその間にDBを変更したら、元のルールに戻るんだ。再度コンプライアンスが確認されるまでね。

SQLiteはRustとは少し違って、データコンテナなんだと思う。これが鍵だね。sqlite.orgからの情報:[2004年以降]、ファイルフォーマットは完全に後方互換性がある。> 「後方互換性」とは、新しいバージョンのSQLiteが常に古いバージョンで作成されたデータベースファイルを読み書きできることを意味する。SQLiteは「前方互換性」もあることが多く、古いバージョンが新しいバージョンで作成されたデータベースファイルを読み書きできることもある。ただし、前方互換性が壊れることもある。新しい機能がファイルフォーマットに追加されることがあるからね。エディション(A)と(B)があるとき、後方互換性はどうなるの?(B)は(A)と後方互換性がある必要があるの?もしそうなら -> エディションは後方互換性があるけど、必ずしも前方互換性があるわけではない、これが今の状況だね:-------(A)----(B)-- もし違うなら -> エディションは後方互換性がない、エディションの空間が二分される:---+----(A)-------- \ --(B)-------- そうなると、(A)..(Z)との後方互換性を心配しなきゃいけなくなる。エディション(Y)からファイルをインポートしたらどうなるの? 1. https://www.sqlite.org/formatchng.html --- 面白い追記、sqlite.orgで「backwards compat」をグレップしてみた: https://pastebin.com/Q7b7h4eM

面白いアイデアですね。ペットピーブのリストの後に、後方互換性を保ったまま「代替デフォルト」のセットを持つための簡単な提案があるのが好きです。オプトインしたくないなら、新しいPRAGMA edition = 2026を実行しなければいいだけです。あまりにも多くの場合、単なる問題のリストと、他の人が変わってくれることを願っているだけになっています。 SQLITE_BUSYの(軽い)擁護として言うと、busy_timeoutはsqliteにスリープしてタイムアウトまで再試行するように指示するだけです。待っている間に他のことができるかもしれない呼び出しコードに任せるのは、ライブラリとしては理にかなったデフォルトだと思います。でも、そのロジックはしばしば見落とされがちですね!

これはペットピーブのリストというより、SQLiteを真剣に使っている人たちがデータベースを設定するほぼ普遍的な方法ですね。これらの提案の代替設定が2026年の間違ったデフォルトである可能性があると提案するのは妥当です。

そうだね。データベースのロックに関する話は、SQLiteに関する根強い神話だよ。すべてのデータベースは書き込み時にロックするけど、そのロックの粒度の問題なんだ。複数のライターは順番に書くって感じ。

Hacker Newsで議論の続きを見る