# SQLiteがバイトコードを使用する理由 - 技術要約 ## 概要 すべてのSQLデータベースエンジンは、入力されたSQLテキストを「プリペアドステートメント」に変換してから実行するという共通のアプローチを取る。プリペアドステートメントの実装方法として、主に2つの手法が存在する: 1. **バイトコード方式**:SQLiteが採用。SQLを仮想マシン言語に変換し、仮想マシンインタープリターで実行 2. **オブジェクトツリー方式**:MySQLやPostgreSQLが採用。SQLを処理を表すオブジェクトツリーに変換し、ツリーを辿って実行 ## 技術的定義 ### SQLiteのバイトコード SQLiteのバイトコードは、従来のJavaVMやWebAssemblyとは異なり、データベース特有の高レベル操作を含む: - **OP_Column**:特定のカーソルが指すデータベース行のN番目の列から値を抽出 - **OP_CreateBtree**:データベースファイル内に新しいB-Treeの領域を割り当て - **OP_ParseSchema**:sqlite_schemaテーブルの再読み込みと再解析 - **OP_SeekGE**:B-Tree上のカーソルを指定キー以上の最初のエントリに移動 - **OP_Next**:B-Tree上のカーソルを次のエントリに進める ### 抽象構文木(AST)とデータフロープログラミング AST(Abstract Syntax Tree)は通常LALR(1)パーサーによってSQL文から生成される。実行可能形式へと変換される過程で、元のAST構造から大きく変化するため、最終的な実行可能オブジェクトツリーを「AST」と呼ぶのは厳密には不正確。 「データフロープログラム」という表現がより適切で、各ノードが特定の計算を担当し、ノード間でデータを受け渡しする有向グラフを形成する。 ## バイトコード方式の技術的優位性 ### 1. 理解しやすさ - 平坦なopcode列として簡単に出力可能 - SQLiteの「EXPLAIN」キーワードにより、6列テーブル形式で可視化 - オブジェクトツリーは多様な構造により統一的な表示が困難 ### 2. デバッグ性 - フロントエンド(解析)とバックエンド(実行)の明確な分離 - `PRAGMA vdbe_trace=ON`によるバイトコード実行トレース機能 - 問題発生時の原因特定が容易 ### 3. 増分実行 - SQL文を部分的に実行し、最初の行を生成後に一時停止可能 - オブジェクトツリーでは実行状態の保存・復元が複雑 - クライアント/サーバー型でないSQLiteには重要な機能 ### 4. メモリ効率 - バイトコードは対応するASTより小さい - `sqlite3_prepare()`実行中は両方が存在するが、ASTは即座に破棄 - プリペアドステートメントの長期キャッシュに有利 ### 5. 実行速度 - 計算の各ステップで必要な判断が少ない - 実験的検証は困難だが、理論的に高速 ## オブジェクトツリー方式の優位性 ### 1. 実行時クエリプラン変更 - バイトコードは生成後の変更が困難 - オブジェクトツリーは実行中の動的調整が可能 - クエリの自己調整機能 ### 2. 並列化容易性 - 各処理ノードを異なるスレッドに割り当て可能 - ノード間の中間結果転送用のスレッドセーフキューが必要 - ノード内での同期プリミティブは不要 - データ利用可能性と出力キューの空きによる単純なスケジューリング ## 適用領域による使い分け - **OLTP(オンライントランザクション処理)**:SQLiteの主要用途、バイトコードが適している - **OLAP(オンライン分析処理)**:大規模マルチコアサーバーでの分析クエリ、データフロープログラムが有利 ## 結論 SQLiteはOLTP用途とIoT環境での使用を主眼とし、増分実行とメモリ効率を重視するため、バイトコード方式を採用している。一方、大規模分析処理や並列処理を重視するシステムでは、オブジェクトツリー方式が適している場合もある。