見出し画像

劔"Tsurugi" の起動高速化の展望

本日、Tsurugi の初の正式バージョンである 1.0.0 をリリースしました。

劔 “Tsurugi” - https://tsurugidb.com/

上記コミュニティサイト右上の GitHub リンクよりダウンロードできます。

本記事では、Tsurugi を起動する際に裏側で行われている処理の紹介と、その高速化の展望についてお話しします。

ここで紹介するコンポーネントやその周辺技術については、刊行済みの書籍 (通称「劔本」) に詳しく掲載しています。
併せてご参照ください。


インメモリデータベースの起動

Tsurugi は、全てのデータをメモリ上に保持することで、高速なデータベース操作を実現しています (インメモリデータベース)。
ただし、それだけでは電源を切るとデータが消えてしまうため、書き込まれた内容のコピー (トランザクションログ) をディスクにも保存しています。

このような構成をとる関係上、Tsurugi を起動する際にはディスクに記録した内容をまずメモリに展開しなければデータベースとして利用できません。
書き込まれた内容が膨大であればこの処理にはかなりの時間を要し、結果として Tsurugi の起動が遅くなってしまいます。

以下は、データベースに書き込んだ件数と、Tsurugi の起動に要した時間の一覧です。

  • 100,000,000 行: 423 秒

  • 200,000,000 行: 843 秒

  • 400,000,000 行: 1,766 秒

  • 800,000,000 行: 3,834 秒

上記は、実行する環境や、書き込んだ行の大きさによって大幅に変化します。
例ではサイズが小さい行を想定して測定したため、より大きな行データを利用する場合にはサイズに比例した時間を要します。

データベースの起動時間削減は、データベースのダウンタイムの削減にもつながり、 Tsurugi の開発チームでも重要な課題としてとらえています。
以降では、この問題に対して Tsurugi 開発チームがどのようなことを検討しているかについて紹介します。

なお、データベースの起動時間に大きな影響があるのは、主に以下の2つのコンポーネントです。

  • ログ・データストア (Limestone, 劔本 15章)

  • トランザクションエンジン (Shirakami, 劔本 14章)

高速化の展望

Tsurugi の起動の高速化手法を紹介するにあたり、まず簡単に Tsurugi が起動時に何を行っているかについて紹介します。

Tsurugi はまず、ディスクに保存されたトランザクションログから、データベースの状態を表す「スナップショット」を作成します。
そもそも、トランザクションログにはデータベースへの「すべての操作」が記録されています。
この中からテーブル上の各行に「最後に行われた操作」のみを抜き出して、それらをかき集めることでデータベース全体の状態を表すスナップショットを構築していきます。

スナップショットを構築したのちは、その内容をメモリ上のインデックスに復元していきます。
全てのデータをインデックス上に復元し終わったら、前回の Tsurugi の終了時と同じ状態になり、データベースとして利用できます。

これら2つの操作、「スナップショットの構築」と「インデックスの復元」は、データベースに書き込まれた内容が多いほど時間を要します。
それでは、個々の操作について、それぞれどのような高速化手法が考えられるかについて紹介します。

トランザクションログの圧縮

まずは、そもそもの入力であるトランザクションログを圧縮する方法を紹介します。

前述の通り、トランザクションログにはデータベースに書き込まれたすべての操作が記録されています。
この記録のうち、最新のものだけが抽出されてスナップショットに書き込まれるため、古い記録は不要になります。

例えば、以下のようなログがあったとします (下に行くほど新しい操作とします)

  1. 行 A を作成し、値 100 を設定

  2. 行 B を作成し、値 200 を設定

  3. 行 A の値を 101 に更新

  4. 行 B の値を 201 に更新

  5. 行 A を削除

これを圧縮して古い記録を取り除くと、以下のようになります。

  1. 行 B を作成し、値 201 を設定

上記のうち、行 A は最終的に削除されるので圧縮後には不要になります。
また、行 B については作成後更新されていますが、最初から更新後の値で作成されたものとして記録することで、操作件数を削減しています。

このように、トランザクションログに含まれる操作件数を減らすことで、スナップショット作成時の処理を簡略化し、起動時間を短縮しています。
また、単純にトランザクションログのサイズが減るため、ディスク容量を節約する意味でも有効です。

ただし、この最適化が効きにくいトランザクションログも存在します。
以下はその例です。

  1. 行 A を作成し、値 100 を設定

  2. 行 B を作成し、値 200 を設定

  3. 行 C を作成し、値 300 を設定

上記のように、すべてが最新の操作であるようなトランザクションログには効果がありません。
なお、ログの圧縮を行った後の構造もすべて最新の操作となっているため、同様です。

Tsurugi 1.0.0 では、トランザクションログを圧縮するためのログコンパクションツールを提供しています。
現在、データベース停止中に圧縮を行う機能が正式にリリースされています。また、データベース起動時に圧縮を行う機能も試験的に提供されています。

それぞれの機能については以下を参照して下さい。

スナップショット作成の並列化

次に、トランザクションログからスナップショットを作成する際に、並列実行することで高速化を図る方法を紹介します。

Tsurugi が生成するログは「Parallel Write-Ahead Logging」と呼ばれる方式を採用しています。
これはトランザクションログを複数のファイルに分割して記録する方式で、Tsurugi のようにトランザクションを高速かつ大量に処理することを目的としたデータベースに有効です。

この分割されたトランザクションログファイルを複数のスレッドで同時に処理することで、スナップショットの作成時間を短縮します。

また、スナップショットの作成は、ログに記載されたすべての操作から行ごとに最新の操作のみを抜き出すため、行単位での並列処理が行いやすい操作です。
Tsurugi 1.0.0 では、この特性を活かしてスナップショット作成の並列化を行う機能を提供しています。

この機能はファイルI/Oやその周辺ライブラリが全体のボトルネックになるため、並列数の増加による高速化はそこまで望めません (引き続き改善予定です)。
現在のところ、並列数は開発チーム内で良好な成績を確認できた8スレッドで行うようにしていますが、以下の開発用のパラメータで変更可能です。

差分スナップショットの作成

Tsurugi 1.0.0 では、データベース起動時にトランザクションログ全体からスナップショットを作成しています。
実はこの手順には無駄が多く、以下のようにすることでスナップショットの作成処理を大幅に短縮できます。

  1. 前回起動時に作成したスナップショットをディスク上に保持しておく

  2. 次回起動時は、トランザクションログのうち「前回に書き込まれた分」のみを抽出し、そこから「スナップショットの差分」を作成する

  3. 前回起動時に作成したスナップショットと、今回起動時に作成したスナップショットの差分を合成し、最新のスナップショットを作成する

現状の Tsurugi では、スナップショットの作成時間はほぼトランザクションログの規模に比例していますが、上記のようにすることで「前回起動時に追加されたトランザクションログの規模」に限定することができます。
また、「トランザクションログの圧縮」で紹介したツールは圧縮されたトランザクションログを作成するようにしていますが、それを「スナップショットの差分」として利用可能な形式を生成するようにすることで、スナップショットの作成時間をさらに短縮することができます。

本機能は現在開発中であり、 Tsurugi GA2 マイルストーン中のリリース (2024年中) を予定しています。

スナップショットの事前作成

次に紹介する「スナップショットの事前作成」は、前述の「差分スナップショットの作成」をさらに推し進めたものです。
データベース起動中にバックグラウンドで定期的に差分スナップショットを作成し、またはそれらのスナップショット同士を定期的に再編することで、データベース起動時にはすでに最新のスナップショットがほぼ完成した状態を保つことができるようになります。

ここまでいけば理論上、データベースの起動時における「スナップショットの作成」にかかる時間をほぼゼロにすることができます。

本機能は、 Tsurugi GA2 マイルストーン中のリリース (2024年中) を予定しています。

インデックス復元の並列化

Tsurugi 1.0.0 では、技術的な理由によりスナップショットからインデックスを復元する際に並列化ができませんでした。
「スナップショット作成の並列化」と同様に、「インデックスの復元」も複数のスレッドで同時に処理することで高速化が可能です。

本機能は、 Tsurugi GA2 マイルストーン中のリリース (2024年中) を予定しています。

差分インデックスの導入

最後に紹介するのは、スナップショットからインデックスの復元を行わないという手法です。

Tsurugi はインメモリインデックスを採用していて、全てのデータをメモリ上に保持しています。
しかし、データの性質によっては、この方式が適さない場合もあります。

例えば、一部のヒストリカルデータなどが該当します。
これらのデータは膨大でメモリ容量を圧迫しますが、実際には比較的最近のデータのみが頻繁にアクセスされるケースが多くあります。
このような場合、最新のデータのみをメモリ上に保持し、それ以外のデータはディスク上に保持する方式が有効です。

現在検討している「差分インデックス」は、比較的最近の書き込みをメモリ上に保持し、それ以外のデータをスナップショットから読み出す方式です。
この方法を採用すると、データベース起動直後のインデックス復元が不要になります。

上記により、データベースの起動時における「インデックスの復元」にかかる時間は、ほぼゼロになります。
ただし、すべてのテーブルを差分インデックスにすると、インメモリインデックスの利点を生かせません。
そのため、大規模なヒストリカルデータ等に適用を絞り、比較的小規模なテーブルは従来通りメモリ上に展開する方式をとるハイブリッド方式を想定しています。
これらは、テーブル作成時に利用者がどちらの方式をとるか指定できるようにすることを計画しています。

また、この機能には別の利点もあり、全てのデータをメモリ上に保持しておく必要がなくなることで、データベースのメモリ使用量を大幅に削減することができます。
もちろん、差分インデックスを利用するテーブルは、メモリ上に存在するテーブルよりも性能が低下しますが、使い方を工夫すればそれを上回るメリットが得られます。

本機能は、 Tsurugi GA3 マイルストーン中のリリース (2024年度中) を予定しています。

この記事が気に入ったらサポートをしてみませんか?