http://rabbitfoot141.hatenablog.com/entry/2020/01/17/012402
自作ストレージエンジンから見るMySQL の内部実装2020/01/18 @lrf141
View Slide
自己紹介- けんつ (@lrf141)- DBMS, 分散システム- Go / PHP / C- MySQL はフェチ- 社会性がたりない
こっち読むと幸せになれる
始める前に
24 枚 / 10 分
38 枚 / 5 分
今日話すこと
今日話すこと- OSS としての mysql-server コード・リーディング- 既存の SE 実装- 内部実装を支えるシステムコール- MySQL Internal Manual- Source Code Documentation
今日話すこと- ストレージエンジンを支える仕組み- テーブルファイル作成時の処理- テーブルファイル読み込み時の処理- SELECT, INSERT 時の処理
資料- MySQL Internal Manual- MySQL 8.0.18 Source Code Documentation- Connpass に掲載したブログ- Github mysql-server
ストレージエンジンを支える仕組み
ストレージエンジンを支える仕組み- Pluggable Storage Engine- Handlerton- Handler- Share- Lock
Pluggable Storage Engine- MySQL はストレージエンジンをプラグインとして扱う- 共有ライブラリ(.so)形式で /usr/lib/mysql/plugin 以下にあるものが対象- 動的に設定可能- mysqld は ha_hoge.cc|.h で宣言、実装されている API を呼び出す
handlerton- Handler Singleton- 各ストレージエンジンにつき1つのみの生成- ストレージエンジンの定義を保持- メタ情報と API を実装したメソッドポインタ
Handler- テーブル、コネクション毎に作成される- データストレージ、インデックス、行操作- ロック等、同時実行制御に関する処理も保持- MySQL Server と実際に通信するのはこれ- SQL 操作以外にも統計情報系もここ
Share- テーブルのメタデータを保持する- インスタンスはテーブル間で共有される- ロックに関するメンバを持つ- ファイルディスクリプタはここで持つと良い?
Lock- Share にマスターロック(THR_LOCK)、ハンドラにロックインスタンス(THR_LOCK_DATA)- ロックインスタンスが他のハンドラに対して有効なロックを持つ- Posix Thread ベース- デフォルトで SELECT は共有ロック、INSERT 等は排他ロック- 内部ロックはロックインスタンスを利用
class Gambit_share : public Handler_share {public:THR_LOCK lock;const char *name;…}class ha_gambit : public handler {THR_LOCK_DATA lock; ///< MySQL lockGambit_share *share; ///< Shared lock infoGambit_share * get_share(); ///< Get the share
TABLE 作成時の処理
Table 作成時の処理- /var/lib/mysql 以下にデータベース名でディレクトリが存在する前提- テーブル毎にディレクトリが作成される- ha_hoge::create を実装する- open システムコールのラッパーである my_create を使用する- sdi ファイルも同時に生成される- 作成時にオプションを変更可能
int ha_gambit::create (const char *name, TABLE *, HA_CREATE_INFO *,dd::Table *) {DBUG_TRACE;File create_file;DBUG_ENTER("ha_gambit::create" );if ((create_file= my_create(name, 0, O_RDWR | O_TRUNC, MYF(0))) < 0)DBUG_RETURN(-1);if ((my_close(create_file, MYF(0))) < 0)DBUG_RETURN(-1);
TABLE 読み込み時の処理
Table 読み込み時の処理- ha_hoge::open を実装する- SELECT, INSERT, etc… 時に走るためロックが必要- ファイルディスクリプタは share でもっておくのが良さそう?
int ha_gambit::open(const char *name, int, uint, const dd::Table *) {DBUG_TRACE;File open_file;if (!(share = get_share())) return 1;thr_lock_data_init(&share->lock, &lock, NULL);if (!(open_file = my_open(name, O_RDWR, MYF(0))))return 1;share->table_file = open_file;share->name = name;return 0;}
SELECT, INSERT 時の処理
共通の前提- Share(TABLE_SHARE) がテーブルのメタデータを保持- TABLE_SHARE は各フィールドの情報を持つ Field も保持- 各フィールドは String(sql_string.h) というバッファを利用- ロックが走る- 内部フォーマットの先頭はカラム数分の Null ビットマップ
for (Field **field = table->field; *field; field++) {const char *p;const char *end;…}
INSERT 時の処理ひたすら Field から String バッファに値を格納してmy_write するだけ!!!!
SELECT 時の処理- Field の値を引数である buf に格納- 値の実体は Field->ptr- buf にコピーするべきだが Field->ptr と buf は参照先が同じ- store メソッドで Field->ptr に値読み取った値を格納する
おわりに
おわりに- MySQL 内でストレージエンジンが担保する機能は少ない- SE は Handler が肝- ロックに関することがわかると大体いける- 全て実装依存- コードはドキュメント