MYDB 0. プロジェクト構成といくつかの言わざるを得ないこと
プロジェクト URL:https://github.com/CN-GuoZiyang/MYDB
Loading repository data...
はじめに(いくつかの雑談)
もしかすると「車輪の再発明」にハマってしまったのかもしれませんし、単にデータベースの基本原理を補強したいと思っただけかもしれません。約半月ほど、仕事終わりから深夜にかけての時間を使って、このプロジェクトを完成させました。
データベースとは少し縁があり、学校で「データベースシステム」という授業があったとき、ちょうど深圳でインターンをしていました。そこで、オンライン授業がインターン期間中の正当なサボり理由となり、授業を聴くだけで他のことはほとんど何もしませんでした。同様に、オペレーティングシステムもその時期に教わりましたが、OS には少し興味があったため、DB ほどひどくはありませんでした。
しかしすぐに副作用が現れました。ByteDance の二次面接で、面接官にデータベースの知識を尋ねられ、正直に「全く知りません」と答えました。さらに Redis についても聞かれ、「これも知りません」と答えざるを得ませんでした。幸い面接官は気にせず合格させてくれましたが、この二つの「知らない」が面接評価を下げたかは不明です……
就職後、チームの業務はデータベースとはあまり関係ありませんでした。これでデータベースや CRUD から離れられると思っていましたが、状況は急変しました。隣の資産管理チームが人手不足で、部署に人員支援を申請し、私が支援に回されました。資産管理は一貫性の要求が高く、以前のように何でも Redis に放り込むわけにはいかず、入らなければ問題なし、というわけにもいきません……
きっかけ
ある日、GitHub を見ていたら、@qw4990 さんのデータベースプロジェクト NYADB2 を偶然見つけました。これは Go 言語で実装されたシンプルなデータベースで、階層設計が非常に優れており、コードも読みやすいです。Java への未練もあり、このプロジェクトの基本構造を参考にして Java 版の DB を書き始めました。実装過程では多くの細部をこのプロジェクトに倣いました。
恥ずかしい話ですが、このプロジェクトは作者の学部時代の趣味プロジェクトであり、これがいわゆる「大物」なのかもしれません(逃
RESPECT
全体構成
MYDB はバックエンドとフロントエンドに分かれており、両者はソケットで通信します。フロントエンド(クライアント)の役割は非常にシンプルで、ユーザー入力を読み取り、バックエンドに送信して実行し、結果を出力し、次の入力を待ちます。MYDB のバックエンドは SQL を解析し、合法的な SQL であれば実行して結果を返します。パーサーを除き、MYDB のバックエンドは 5 つのモジュールに分かれており、それぞれ一定の役割を持ち、インターフェースを通じて依存するモジュールにメソッドを提供します。5 つのモジュールは以下の通りです:
- トランザクションマネージャー(TM)
- データマネージャー(DM)
- バージョンマネージャー(VM)
- インデックスマネージャー(IM)
- テーブルマネージャー(TBM)
モジュール間の依存関係は以下の通りです:

この依存図からトポロジカルソートをすれば実装順序がわかります。本チュートリアルの実装順は TM -> DM -> VM -> IM -> TBM です。
各モジュールの役割は以下の通りです:
- TM は XID ファイルを管理してトランザクションの状態を維持し、他のモジュールが特定のトランザクションの状態を問い合わせるためのインターフェースを提供します。
- DM はデータベースの DB ファイルとログファイルを直接管理します。主な役割は:1) DB ファイルのページ管理とキャッシュ;2) ログファイルの管理、エラー発生時にログから復旧可能にする;3) DB ファイルを DataItem として抽象化し上位モジュールに提供し、キャッシュも行うこと。
- VM は 2 相ロックプロトコルに基づきスケジューリングの直列化を実現し、MVCC を実装して読み書きのブロックを排除します。同時に 2 種類の隔離レベルを実装しています。
- IM は B+ 木に基づくインデックスを実装しています。ちなみに現時点では WHERE 句はインデックスがあるフィールドのみ対応しています。
- TBM はフィールドとテーブルの管理を実装し、SQL 文を解析して文に応じてテーブルを操作します。
開発環境と実行例
プロジェクトの開発には WSL2 と JDK11 を使用しています。Windows 上で実行する場合は起動パラメータ内のパスを Windows 形式に置き換え、JDK のバージョンは 11 以上を保証してください。JDK8 は非対応です(あるいは非対応のメソッドを自分で探して互換性を持たせてください。非対応なのは一部のメソッドだけのはずです)。
現在は JDK8 にも対応済みです
ほぼすべてのモジュールとサブモジュールには test フォルダ内に対応する単体テストがあります。皆さんもぜひ単体テストをたくさん書いてください。書かないと後でバグがどこから来たかわからなくなります。
テストを書かずに一時的に快適でも、バグが出たら地獄です(
まず pom.xml でコンパイルバージョンを調整し、IDE にインポートする場合はプロジェクトのコンパイルバージョンを自分の JDK に合わせて変更してください。
まず以下のコマンドでソースコードをコンパイルします:
mvn compile
次に以下のコマンドで /tmp/mydb をパスとしてデータベースを作成します:
mvn exec:java -Dexec.mainClass="top.guoziyang.mydb.backend.Launcher" -Dexec.args="-create /tmp/mydb"
続いて以下のコマンドでデフォルトパラメータでデータベースサービスを起動します:
mvn exec:java -Dexec.mainClass="top.guoziyang.mydb.backend.Launcher" -Dexec.args="-open /tmp/mydb"
これでデータベースサービスはローカルの 9999 ポートで起動しています。別のターミナルを開き、以下のコマンドでクライアントを起動してデータベースに接続します:
mvn exec:java -Dexec.mainClass="top.guoziyang.mydb.client.Launcher"
対話式コマンドラインが起動し、ここで SQL 風の文法を入力すると、エンターで文がサービスに送信され、実行結果が表示されます。
実行例:
