Upgrade to PRO for Only $50/Year—Limited-Time Offer! 🔥
Speaker Deck
Features
Speaker Deck
PRO
Sign in
Sign up for free
Search
Search
PHPでデータベースを作ってみた/create-data-with-php
Search
Ryo Tomidokoro
June 22, 2024
Technology
11
11k
PHPでデータベースを作ってみた/create-data-with-php
PHPカンファレンス福岡2024の登壇資料です。
Ryo Tomidokoro
June 22, 2024
Tweet
Share
More Decks by Ryo Tomidokoro
See All by Ryo Tomidokoro
開発者が知っておきたい複雑さの正体/where-the-complexity-comes-from
hanhan1978
7
3k
Spec Driven Development入門/spec_driven_development_for_learners
hanhan1978
2
1.2k
フロントエンドがTypeScriptなら、バックエンドはPHPでもいいじゃない/php-is-not-bad
hanhan1978
8
13k
どうすると生き残れないのか/how-not-to-survive
hanhan1978
17
14k
100分で本番デプロイ!Laravelで作るWebアプリケーション作成/100min_web_app_cicd
hanhan1978
1
220
PHPerのための計算量入門/Complexity101 for PHPer
hanhan1978
8
3.3k
集中して作業する技術/how_to_work_deeply
hanhan1978
65
53k
ADRを一年運用してみた/adr_after_a_year
hanhan1978
8
4.6k
B+木入門:PHPで理解する データベースインデックスの仕組み/b-plus-tree-101
hanhan1978
5
5.7k
Other Decks in Technology
See All in Technology
非CUDAの悲哀 〜Claude Code と挑んだ image to 3D “Hunyuan3D”を EVO-X2(Ryzen AI Max+395)で動作させるチャレンジ〜
hawkymisc
1
170
Lambdaの常識はどう変わる?!re:Invent 2025 before after
iwatatomoya
1
440
[CMU-DB-2025FALL] Apache Fluss - A Streaming Storage for Real-Time Lakehouse
jark
0
110
法人支出管理領域におけるソフトウェアアーキテクチャに基づいたテスト戦略の実践
ogugu9
1
220
Debugging Edge AI on Zephyr and Lessons Learned
iotengineer22
0
160
生成AIでテスト設計はどこまでできる? 「テスト粒度」を操るテーラリング術
shota_kusaba
0
660
【AWS re:Invent 2025速報】AIビルダー向けアップデートをまとめて解説!
minorun365
4
490
乗りこなせAI駆動開発の波
eltociear
1
1.1k
LLM-Readyなデータ基盤を高速に構築するためのアジャイルデータモデリングの実例
kashira
0
230
今年のデータ・ML系アップデートと気になるアプデのご紹介
nayuts
1
250
グレートファイアウォールを自宅に建てよう
ctes091x
0
140
Karate+Database RiderによるAPI自動テスト導入工数をCline+GitLab MCPを使って2割削減を目指す! / 20251206 Kazuki Takahashi
shift_evolve
PRO
1
670
Featured
See All Featured
A designer walks into a library…
pauljervisheath
210
24k
Build your cross-platform service in a week with App Engine
jlugia
234
18k
Chrome DevTools: State of the Union 2024 - Debugging React & Beyond
addyosmani
9
1k
Rails Girls Zürich Keynote
gr2m
95
14k
Improving Core Web Vitals using Speculation Rules API
sergeychernyshev
21
1.3k
Leading Effective Engineering Teams in the AI Era
addyosmani
8
1.3k
GraphQLの誤解/rethinking-graphql
sonatard
73
11k
Creating an realtime collaboration tool: Agile Flush - .NET Oxford
marcduiker
35
2.3k
GraphQLとの向き合い方2022年版
quramy
50
14k
Imperfection Machines: The Place of Print at Facebook
scottboms
269
13k
Practical Tips for Bootstrapping Information Extraction Pipelines
honnibal
25
1.6k
[SF Ruby Conf 2025] Rails X
palkan
0
500
Transcript
PHPでデータベースを作ってみた @hanhan1978 PHPカンファレンス福岡2024
@hanhan1978 名前 富所 亮 所属 株式会社カオナビ CTO室 BackEnd Re-architecturing Team
(BERT) Blog https://blog.hanhans.net Podcast https://podcasters.spotify.com/pod/show/yokohama-north-am 2
免責事項
この登壇への注意 本発表はRDBMSっぽく振る舞うプログラムを 気軽なノリで作っていくお話です。
この登壇への注意 • パーサー、オプティマイザ • ストレージエンジン • などなどのRDBMSの構成要素 取り扱いません!
そもそも目的は!?
目的 • データベースを深く理解する • プロトコルを深く理解する • プログラムへの解像度を上げる
目的 • データベースを深く理解する • プロトコルを深く理解する • プログラムへの解像度を上げる 全部ウソ!
(答え)なんとなく作ってみたかった
ではデータベースを自作するための 参考資料とは?
参考書1 WEB+DB PRESS Vol.122 作って学ぶRDBMSのしくみ - 技術評論社 2021
参考書2 詳説データベース - ストレージエンジンと分散データシステムの仕組み - O’Reilly 2021
参考書3 Software Design 2024年6月号 - 技術評論社
参考書4 https://speakerdeck.com/hanhan1978/b-plus-tree-101
否!!
まじめに作ろうとすると 大変な努力が必要になる
もっと迂闊に作りたい
真の参考書 ゼロからトースターを作ってみた結果 - 新潮文庫 2015
不格好でもいいので、動く完成品を作ること は学びにとって実に良いことです
駄目な例 Learn to draw fast - https://www.alibati.com/work/horse
動く完成品を作ろう https://blog.crisp.se/2016/01/25/henrikkniberg/making-sense-of-mvp
目標の具体化 ここまで4分
MySQLと何なのか?
PHPからみたMySQL よくみるPDOのサンプルコード
PHPからみたMySQL 3306ポートでTCP/IPのソケット通信
つまり
目標 同じ通信内容ならPHPからはデータベースに見える!
目標を具体化 1. 特定のポート番号でソケット通信を行う 2. MySQLと同じ通信内容を行う データベースを作る
余談
この考え方は様々なミドルウェアに対応 • Redis • ElasticSearch • Apache ようするにTCP/IPでソケット通信を行うアプリケーションは 既存の挙動を真似ることさえ出来れば同じように作れる
では!作っていく!
1. 特定のポート番号でソケット通信を行う 2. MySQLと同じ通信内容を行う やること
1. 特定のポート番号でソケット通信を行う 2. MySQLと同じ通信内容を行う やること ネットワークプログラミング
参考書 UNIXネットワークプログラミング Vol.1 ※もっと新しい本 でよいです
echoサーバー 今回の用途であれば 「echoサーバー」という検索ワードを使うと サンプル実装がいっぱい引っかかります
echoサーバー 今回の用途であれば 「echoサーバー」という検索ワードを使うと サンプル実装がいっぱい引っかかります が!
2年前に書いておきました PHPerKaigi2022 - パンフレット
具体的なコード
実際に起動してみると... うごくぞ...
MySQLの場合 3306番で動作してる
TCP/IPの状態確認 Mac Linux
1. 特定のポート番号でソケット通信を行う 2. MySQLと同じ通信内容を行う やること 進捗50%!
1. 特定のポート番号でソケット通信を行う 2. MySQLと同じ通信内容を行う やること
1. 特定のポート番号でソケット通信を行う 2. MySQLと同じ通信内容を行う やること ???どうやんの???
通信内容をみるには? • Wireshark • tcpdump • ngrep
通信内容をみるには? • Wireshark • tcpdump • ngrep 今回はお手軽な ngrep を採用
さっそくMySQLの通信を見る -x -q -d 通信の中身を16進数表記でdump、かつ右側にテキスト表示 header, payload 以外の通信内容はdumpしない 通信をみるネットワークインタフェースを指定
さっそくMySQLの通信を見る 引数は match と filter 上の例は match は無指定、filter に port
3306 を指定 これで3306番ポートにまつわるローカル ホストの通信が確認できる
通信内容をみる1
通信内容をみる1 まずサーバー側からスタート
通信内容をみる1 クライアントの認証リクエスト
通信内容をみる1 サーバー側からのOK応答
こんなもの読めるのか?
MySQL公式ドキュメント https://dev.mysql.com/doc/dev/mysql-server/latest/
Handshake https://dev.mysql.com/doc/dev/mysql-server/latest/page_protocol_connection_phase.html
Protocol::HandshakeV10
これで読めそうやん!
プロトコル読解 - Handshake
プロトコル読解 - Handshake いきなり違う!
おちつけ!
MySQL Packets
MySQL Packets 基本仕様
プロトコル読解 - Handshake 最初の3Byteは payloadのサイズ ▶ 4a 00 00 ▶
74 Byte 次の1ByteはシーケンスID ▶ 00 ▶ 0
プロトコル読解 - Handshake このパケットは Header 4 Byte + Payload 74
Byte の 78 Bytea MySQLのパケットを考える際は常にこの Header を考慮にいれる
プロトコル読解 - Handshake 0x0a ▶ 10進数の10
プロトコル読解 - Handshake サーバーバージョン文字列
ASCIIコード表 https://www.sciencebuddies.org/science-fair-projects/references/ascii-table
ASCIIコード表 https://www.sciencebuddies.org/science-fair-projects/references/ascii-table 8 ▶ 0x38
サーバーからの認証OKパケット
通信内容をみる1 サーバー側からのOK応答
General Response Packets
プロトコル読解 - General Response 7ByteのPayload header ▶ 0x00 affected_rows ▶
0x00 last_insert_id ▶ 0x00
プロトコル読解 - General Response 7ByteのPayload header ▶ 0x00 affected_rows ▶
0x00 last_insert_id ▶ 0x00 君はここにいたのか!!
PDO::lastInsertId https://www.php.net/manual/ja/pdo.lastinsertid.php
読める!読めるぞ!
だが、このまま細かくプロトコルを 実装するのは骨が折れる 第一面倒くさい。俺はデータベースっ ぽいやつを早く作りたい
こうして...
こうじゃ! ※良い子はマネしない
さらに、こうして...
こうじゃ!!!! ※良い子はマネしない
クライアント側のソースコード なんと、こんなものでも PDOをコロッとだますと接続できる
このままDEMOになだれ込みたいが... 結果セット返却の解説は必要 もうちょっと我慢してくれ 残り15分以上
SELECTの通信
SELECTの通信 Header ▶ Payloadサイズは37
SELECTの通信 Header ▶ Payloadサイズは37 Payload ▶ 先頭に 0x03 ▶ ETX
制御コード
SELECTの通信 Header ▶ Payloadサイズは37 Payload ▶ 先頭に 0x03 ▶ ETX
制御コード Payload残り ▶ クエリの文字列 SELECT id, value1, value2 FROM items
SELECTの通信 結果セットの送信パケットを適切に理解することが大切 このパケットは合計8つのMySQLパケットで構成されている
SELECTの通信 1Byte の Payload 最初のパケットは結果セットのカラム数 0x03
SELECTの通信 40Byte の Payload 2番目以降はカラム定義 x コラム数のパケット
SELECTの通信 40Byte の Payload 03 646566 ▶ 3byte の文字列メッセージ def
固定値
SELECTの通信 40Byte の Payload 04 74657374▶ 4byte の文字列メッセージ test データベース名
SELECTの通信 40Byte の Payload 04 74657374▶ 4byte の文字列メッセージ test データベース名
05 6974656d73▶ 5byte の文字列メッセージ items テーブル名 05 6974656d73▶ 5byte の文字列メッセージ items オリジナルのテーブル名
SELECTの通信 40Byte の Payload 04 74657374▶ 4byte の文字列メッセージ test データベース名
05 6974656d73▶ 5byte の文字列メッセージ items テーブル名 05 6974656d73▶ 5byte の文字列メッセージ items オリジナルのテーブル名 02 6964▶ 2byte の文字列メッセージ id カラム名 02 6964▶ 2byte の文字列メッセージ id オリジナルのカラム名
SELECTの通信 40Byte の Payload 0c ▶ 固定値 3f00 ▶ キャラセット
binary
SELECTの通信 40Byte の Payload 0c ▶ 固定値 3f00 ▶ キャラセット
binary 0b000000 ▶ カラムサイズ int(11)
SELECTの通信 40Byte の Payload 0c ▶ 固定値 3f00 ▶ キャラセット
binary 0b000000 ▶ カラムサイズ int(11) 03 ▶ データ型
SELECTの通信 40Byte の Payload 0c ▶ 固定値 3f00 ▶ キャラセット
binary 0b000000 ▶ カラムサイズ int(11) 03 ▶ データ型 0350 ▶ ビット演算されたフラグ
SELECTの通信 40Byte の Payload 0c ▶ 固定値 3f00 ▶ キャラセット
binary 0b000000 ▶ カラムサイズ int(11) 03 ▶ データ型 0350 ▶ ビット演算されたフラグ 000000 ▶ Packet終端
SELECTの通信 結果で返却するカラム数の数だけカラム定義のPacketを返す 今回は3カラム分
参考資料 - データタイプ https://github.com/mysql/mysql-server/blob/trunk/include/field_types.h
参考資料 - データタイプ https://github.com/mysql/mysql-server/blob/trunk/include/field_types.h
参考資料 https://dev.mysql.com/doc/dev/mysql-server/latest/group__group__cs__column__definition__flags.html
フラグのビット演算例 NOT NULL 0110
フラグのビット演算例 PRIMARY KEY 0350
SELECTの通信 カラム定義と結果セットの間のデリメター(だと思う) fe00002200 ▶ シーケンス番号以外は固定
SELECTの通信 結果行 - 1行目 0131 ▶ idカラムの結果 文字列 1 05686f676531
▶ value1カラムの結果 文字列 hoge1 05686f676532 ▶ value2カラムの結果 文字列 hoge2
SELECTの通信 結果行 - 1行目 0131 ▶ idカラムの結果 文字列 1 05686f676531
▶ value1カラムの結果 文字列 hoge1 05686f676532 ▶ value2カラムの結果 文字列 hoge2 intは文字列で返ってくる PDO, ORMなどで型変換の不具合イシューが上がっ てくるのはここらへんの仕様が関係ありそう
SELECTの通信 最後にもう一回デリミタが入ってくる 以上で長かった結果セットの説明も終わりです。
簡単にまとめると... Handshake ▶ OKパケット DML, 更新系クエリ ▶ OKパケット 参照系クエリ ▶
カラム定義を含む、結果セットのパケット 細かくはもっといろいろあるのだけど 偽データベースを動作させるには十分
余談
クエリのパース https://github.com/greenlion/PHP-SQL-Parser
クエリのパース ここまで理解できれば、作れるはず だ!雑なRDBMSが!
ore no database
ore no database
ore no database
DEMO 残り2分
今後の展開 • テストを拡充 • データの保存・読取 • IO多重化 • インデックス •
Information Schema対応
クエリのパース 最低限の動作を満たした動くDB そこから正しい形に整形していけばいい
まとめ
自作は楽しい!
みんなも変なモノ作っていこうな!