PHPでデータベースを作ってみた/create-data-with-php
by
Ryo Tomidokoro
Link
Embed
Share
Beginning
This slide
Copy link URL
Copy link URL
Copy iframe embed code
Copy iframe embed code
Copy javascript embed code
Copy javascript embed code
Share
Tweet
Share
Tweet
Slide 1
Slide 1 text
PHPでデータベースを作ってみた @hanhan1978 PHPカンファレンス福岡2024
Slide 2
Slide 2 text
@hanhan1978 名前 富所 亮 所属 株式会社カオナビ CTO室 BackEnd Re-architecturing Team (BERT) Blog https://blog.hanhans.net Podcast https://podcasters.spotify.com/pod/show/yokohama-north-am 2
Slide 3
Slide 3 text
免責事項
Slide 4
Slide 4 text
この登壇への注意 本発表はRDBMSっぽく振る舞うプログラムを 気軽なノリで作っていくお話です。
Slide 5
Slide 5 text
この登壇への注意 ● パーサー、オプティマイザ ● ストレージエンジン ● などなどのRDBMSの構成要素 取り扱いません!
Slide 6
Slide 6 text
そもそも目的は!?
Slide 7
Slide 7 text
目的 ● データベースを深く理解する ● プロトコルを深く理解する ● プログラムへの解像度を上げる
Slide 8
Slide 8 text
目的 ● データベースを深く理解する ● プロトコルを深く理解する ● プログラムへの解像度を上げる 全部ウソ!
Slide 9
Slide 9 text
(答え)なんとなく作ってみたかった
Slide 10
Slide 10 text
ではデータベースを自作するための 参考資料とは?
Slide 11
Slide 11 text
参考書1 WEB+DB PRESS Vol.122 作って学ぶRDBMSのしくみ - 技術評論社 2021
Slide 12
Slide 12 text
参考書2 詳説データベース - ストレージエンジンと分散データシステムの仕組み - O’Reilly 2021
Slide 13
Slide 13 text
参考書3 Software Design 2024年6月号 - 技術評論社
Slide 14
Slide 14 text
参考書4 https://speakerdeck.com/hanhan1978/b-plus-tree-101
Slide 15
Slide 15 text
否!!
Slide 16
Slide 16 text
まじめに作ろうとすると 大変な努力が必要になる
Slide 17
Slide 17 text
もっと迂闊に作りたい
Slide 18
Slide 18 text
真の参考書 ゼロからトースターを作ってみた結果 - 新潮文庫 2015
Slide 19
Slide 19 text
不格好でもいいので、動く完成品を作ること は学びにとって実に良いことです
Slide 20
Slide 20 text
駄目な例 Learn to draw fast - https://www.alibati.com/work/horse
Slide 21
Slide 21 text
動く完成品を作ろう https://blog.crisp.se/2016/01/25/henrikkniberg/making-sense-of-mvp
Slide 22
Slide 22 text
目標の具体化 ここまで4分
Slide 23
Slide 23 text
MySQLと何なのか?
Slide 24
Slide 24 text
PHPからみたMySQL よくみるPDOのサンプルコード
Slide 25
Slide 25 text
PHPからみたMySQL 3306ポートでTCP/IPのソケット通信
Slide 26
Slide 26 text
つまり
Slide 27
Slide 27 text
目標 同じ通信内容ならPHPからはデータベースに見える!
Slide 28
Slide 28 text
目標を具体化 1. 特定のポート番号でソケット通信を行う 2. MySQLと同じ通信内容を行う データベースを作る
Slide 29
Slide 29 text
余談
Slide 30
Slide 30 text
この考え方は様々なミドルウェアに対応 ● Redis ● ElasticSearch ● Apache ようするにTCP/IPでソケット通信を行うアプリケーションは 既存の挙動を真似ることさえ出来れば同じように作れる
Slide 31
Slide 31 text
では!作っていく!
Slide 32
Slide 32 text
1. 特定のポート番号でソケット通信を行う 2. MySQLと同じ通信内容を行う やること
Slide 33
Slide 33 text
1. 特定のポート番号でソケット通信を行う 2. MySQLと同じ通信内容を行う やること ネットワークプログラミング
Slide 34
Slide 34 text
参考書 UNIXネットワークプログラミング Vol.1 ※もっと新しい本 でよいです
Slide 35
Slide 35 text
echoサーバー 今回の用途であれば 「echoサーバー」という検索ワードを使うと サンプル実装がいっぱい引っかかります
Slide 36
Slide 36 text
echoサーバー 今回の用途であれば 「echoサーバー」という検索ワードを使うと サンプル実装がいっぱい引っかかります が!
Slide 37
Slide 37 text
2年前に書いておきました PHPerKaigi2022 - パンフレット
Slide 38
Slide 38 text
具体的なコード
Slide 39
Slide 39 text
実際に起動してみると... うごくぞ...
Slide 40
Slide 40 text
MySQLの場合 3306番で動作してる
Slide 41
Slide 41 text
TCP/IPの状態確認 Mac Linux
Slide 42
Slide 42 text
1. 特定のポート番号でソケット通信を行う 2. MySQLと同じ通信内容を行う やること 進捗50%!
Slide 43
Slide 43 text
1. 特定のポート番号でソケット通信を行う 2. MySQLと同じ通信内容を行う やること
Slide 44
Slide 44 text
1. 特定のポート番号でソケット通信を行う 2. MySQLと同じ通信内容を行う やること ???どうやんの???
Slide 45
Slide 45 text
通信内容をみるには? ● Wireshark ● tcpdump ● ngrep
Slide 46
Slide 46 text
通信内容をみるには? ● Wireshark ● tcpdump ● ngrep 今回はお手軽な ngrep を採用
Slide 47
Slide 47 text
さっそくMySQLの通信を見る -x -q -d 通信の中身を16進数表記でdump、かつ右側にテキスト表示 header, payload 以外の通信内容はdumpしない 通信をみるネットワークインタフェースを指定
Slide 48
Slide 48 text
さっそくMySQLの通信を見る 引数は match と filter 上の例は match は無指定、filter に port 3306 を指定 これで3306番ポートにまつわるローカル ホストの通信が確認できる
Slide 49
Slide 49 text
通信内容をみる1
Slide 50
Slide 50 text
通信内容をみる1 まずサーバー側からスタート
Slide 51
Slide 51 text
通信内容をみる1 クライアントの認証リクエスト
Slide 52
Slide 52 text
通信内容をみる1 サーバー側からのOK応答
Slide 53
Slide 53 text
こんなもの読めるのか?
Slide 54
Slide 54 text
MySQL公式ドキュメント https://dev.mysql.com/doc/dev/mysql-server/latest/
Slide 55
Slide 55 text
Handshake https://dev.mysql.com/doc/dev/mysql-server/latest/page_protocol_connection_phase.html
Slide 56
Slide 56 text
Protocol::HandshakeV10
Slide 57
Slide 57 text
これで読めそうやん!
Slide 58
Slide 58 text
プロトコル読解 - Handshake
Slide 59
Slide 59 text
プロトコル読解 - Handshake いきなり違う!
Slide 60
Slide 60 text
おちつけ!
Slide 61
Slide 61 text
MySQL Packets
Slide 62
Slide 62 text
MySQL Packets 基本仕様
Slide 63
Slide 63 text
プロトコル読解 - Handshake 最初の3Byteは payloadのサイズ ▶ 4a 00 00 ▶ 74 Byte 次の1ByteはシーケンスID ▶ 00 ▶ 0
Slide 64
Slide 64 text
プロトコル読解 - Handshake このパケットは Header 4 Byte + Payload 74 Byte の 78 Bytea MySQLのパケットを考える際は常にこの Header を考慮にいれる
Slide 65
Slide 65 text
プロトコル読解 - Handshake 0x0a ▶ 10進数の10
Slide 66
Slide 66 text
プロトコル読解 - Handshake サーバーバージョン文字列
Slide 67
Slide 67 text
ASCIIコード表 https://www.sciencebuddies.org/science-fair-projects/references/ascii-table
Slide 68
Slide 68 text
ASCIIコード表 https://www.sciencebuddies.org/science-fair-projects/references/ascii-table 8 ▶ 0x38
Slide 69
Slide 69 text
サーバーからの認証OKパケット
Slide 70
Slide 70 text
通信内容をみる1 サーバー側からのOK応答
Slide 71
Slide 71 text
General Response Packets
Slide 72
Slide 72 text
プロトコル読解 - General Response 7ByteのPayload header ▶ 0x00 affected_rows ▶ 0x00 last_insert_id ▶ 0x00
Slide 73
Slide 73 text
プロトコル読解 - General Response 7ByteのPayload header ▶ 0x00 affected_rows ▶ 0x00 last_insert_id ▶ 0x00 君はここにいたのか!!
Slide 74
Slide 74 text
PDO::lastInsertId https://www.php.net/manual/ja/pdo.lastinsertid.php
Slide 75
Slide 75 text
読める!読めるぞ!
Slide 76
Slide 76 text
だが、このまま細かくプロトコルを 実装するのは骨が折れる 第一面倒くさい。俺はデータベースっ ぽいやつを早く作りたい
Slide 77
Slide 77 text
こうして...
Slide 78
Slide 78 text
こうじゃ! ※良い子はマネしない
Slide 79
Slide 79 text
さらに、こうして...
Slide 80
Slide 80 text
こうじゃ!!!! ※良い子はマネしない
Slide 81
Slide 81 text
クライアント側のソースコード なんと、こんなものでも PDOをコロッとだますと接続できる
Slide 82
Slide 82 text
このままDEMOになだれ込みたいが... 結果セット返却の解説は必要 もうちょっと我慢してくれ 残り15分以上
Slide 83
Slide 83 text
SELECTの通信
Slide 84
Slide 84 text
SELECTの通信 Header ▶ Payloadサイズは37
Slide 85
Slide 85 text
SELECTの通信 Header ▶ Payloadサイズは37 Payload ▶ 先頭に 0x03 ▶ ETX 制御コード
Slide 86
Slide 86 text
SELECTの通信 Header ▶ Payloadサイズは37 Payload ▶ 先頭に 0x03 ▶ ETX 制御コード Payload残り ▶ クエリの文字列 SELECT id, value1, value2 FROM items
Slide 87
Slide 87 text
SELECTの通信 結果セットの送信パケットを適切に理解することが大切 このパケットは合計8つのMySQLパケットで構成されている
Slide 88
Slide 88 text
SELECTの通信 1Byte の Payload 最初のパケットは結果セットのカラム数 0x03
Slide 89
Slide 89 text
SELECTの通信 40Byte の Payload 2番目以降はカラム定義 x コラム数のパケット
Slide 90
Slide 90 text
SELECTの通信 40Byte の Payload 03 646566 ▶ 3byte の文字列メッセージ def 固定値
Slide 91
Slide 91 text
SELECTの通信 40Byte の Payload 04 74657374▶ 4byte の文字列メッセージ test データベース名
Slide 92
Slide 92 text
SELECTの通信 40Byte の Payload 04 74657374▶ 4byte の文字列メッセージ test データベース名 05 6974656d73▶ 5byte の文字列メッセージ items テーブル名 05 6974656d73▶ 5byte の文字列メッセージ items オリジナルのテーブル名
Slide 93
Slide 93 text
SELECTの通信 40Byte の Payload 04 74657374▶ 4byte の文字列メッセージ test データベース名 05 6974656d73▶ 5byte の文字列メッセージ items テーブル名 05 6974656d73▶ 5byte の文字列メッセージ items オリジナルのテーブル名 02 6964▶ 2byte の文字列メッセージ id カラム名 02 6964▶ 2byte の文字列メッセージ id オリジナルのカラム名
Slide 94
Slide 94 text
SELECTの通信 40Byte の Payload 0c ▶ 固定値 3f00 ▶ キャラセット binary
Slide 95
Slide 95 text
SELECTの通信 40Byte の Payload 0c ▶ 固定値 3f00 ▶ キャラセット binary 0b000000 ▶ カラムサイズ int(11)
Slide 96
Slide 96 text
SELECTの通信 40Byte の Payload 0c ▶ 固定値 3f00 ▶ キャラセット binary 0b000000 ▶ カラムサイズ int(11) 03 ▶ データ型
Slide 97
Slide 97 text
SELECTの通信 40Byte の Payload 0c ▶ 固定値 3f00 ▶ キャラセット binary 0b000000 ▶ カラムサイズ int(11) 03 ▶ データ型 0350 ▶ ビット演算されたフラグ
Slide 98
Slide 98 text
SELECTの通信 40Byte の Payload 0c ▶ 固定値 3f00 ▶ キャラセット binary 0b000000 ▶ カラムサイズ int(11) 03 ▶ データ型 0350 ▶ ビット演算されたフラグ 000000 ▶ Packet終端
Slide 99
Slide 99 text
SELECTの通信 結果で返却するカラム数の数だけカラム定義のPacketを返す 今回は3カラム分
Slide 100
Slide 100 text
参考資料 - データタイプ https://github.com/mysql/mysql-server/blob/trunk/include/field_types.h
Slide 101
Slide 101 text
参考資料 - データタイプ https://github.com/mysql/mysql-server/blob/trunk/include/field_types.h
Slide 102
Slide 102 text
参考資料 https://dev.mysql.com/doc/dev/mysql-server/latest/group__group__cs__column__definition__flags.html
Slide 103
Slide 103 text
フラグのビット演算例 NOT NULL 0110
Slide 104
Slide 104 text
フラグのビット演算例 PRIMARY KEY 0350
Slide 105
Slide 105 text
SELECTの通信 カラム定義と結果セットの間のデリメター(だと思う) fe00002200 ▶ シーケンス番号以外は固定
Slide 106
Slide 106 text
SELECTの通信 結果行 - 1行目 0131 ▶ idカラムの結果 文字列 1 05686f676531 ▶ value1カラムの結果 文字列 hoge1 05686f676532 ▶ value2カラムの結果 文字列 hoge2
Slide 107
Slide 107 text
SELECTの通信 結果行 - 1行目 0131 ▶ idカラムの結果 文字列 1 05686f676531 ▶ value1カラムの結果 文字列 hoge1 05686f676532 ▶ value2カラムの結果 文字列 hoge2 intは文字列で返ってくる PDO, ORMなどで型変換の不具合イシューが上がっ てくるのはここらへんの仕様が関係ありそう
Slide 108
Slide 108 text
SELECTの通信 最後にもう一回デリミタが入ってくる 以上で長かった結果セットの説明も終わりです。
Slide 109
Slide 109 text
簡単にまとめると... Handshake ▶ OKパケット DML, 更新系クエリ ▶ OKパケット 参照系クエリ ▶ カラム定義を含む、結果セットのパケット 細かくはもっといろいろあるのだけど 偽データベースを動作させるには十分
Slide 110
Slide 110 text
余談
Slide 111
Slide 111 text
クエリのパース https://github.com/greenlion/PHP-SQL-Parser
Slide 112
Slide 112 text
クエリのパース ここまで理解できれば、作れるはず だ!雑なRDBMSが!
Slide 113
Slide 113 text
ore no database
Slide 114
Slide 114 text
ore no database
Slide 115
Slide 115 text
ore no database
Slide 116
Slide 116 text
DEMO 残り2分
Slide 117
Slide 117 text
今後の展開 ● テストを拡充 ● データの保存・読取 ● IO多重化 ● インデックス ● Information Schema対応
Slide 118
Slide 118 text
クエリのパース 最低限の動作を満たした動くDB そこから正しい形に整形していけばいい
Slide 119
Slide 119 text
まとめ
Slide 120
Slide 120 text
自作は楽しい!
Slide 121
Slide 121 text
みんなも変なモノ作っていこうな!