MySQLユーザーのためのMySQLプロトコル入門

さいきんMySQLユーザーのためのほげほげ、みたいなのが巷で流行しているようなので暇つぶしがてらに読んでいるMySQLプロトコルについて書いてみようかと思います。

いやまぁ、こういうプロトコルが読めるからといってすごく役立つということは全くないんですが、お酒の席のネタにできたり、高速、簡単、無料で試せるRDS MySQLからRedshiftへのデータ同期に出てくるようなreplicationをいじったツールとかのメンテが容易にできるかもしれなかったり、俺mysqldだぜ、みたいな事ができたり、なんかよくわからないけどちょっとハッピーになれそうですね!

今日は手始めにMySQLとmysql clientがどういう通信をしているのか見ていき、実際にInitial Handshake Packetをparseしてみるところまでをやってみます。

Max OSXでのセットアップ

普段homebrewを使っているのでmysqlとngrepをインストールしてみます。ngrepはお手軽なtcpdumpだと思っていただければOKです。

それではmysqldを起動してngrepを使ってみましょう

するとngrepをしていたターミナルで下記のような結果が得られると思います。

mysqldがlistenしているportに接続するとServerからInitial Handshake Packetが送られてきます。この情報を元にClientはHandshakeResponse41のPacketを生成して返送し、Server側で認証がとおればTextProtocolのやり取りに進むことが出来ます。

通信メッセージの単位 - MySQL Packet

通信を行う際はMySQL Packetというメッセージの単位でやり取りをすることになります。MySQL Packetは4byteのheader + payloadという構成になっており、dev.mysql.comの資料から引用すると:

size name description
int<3> payload_length Length of the payload. The number of bytes in the packet beyond the initial 4 bytes that make up the packet header.
int<1> sequence_id Sequence ID
string payload [len=payload_length] payload of the packet

4byteしかないのでさっくりparseできそうですね。
それでは先ほどngrepで表示されていたpacketをMySQL Packetとして手でparseしてみましょう

MySQL Packetは3byteのpayload length, 1byteのsequence id、payload length - 4がpayloadとなるので

となるわけですね。それではpayloadを更にparseしてみましょう

Connection Phase Packet

接続した際に最初にServerから送られてくるMySQL PacketがConnection Phase Packetとなります。再びdev.mysql.comの資料から引用します:

Connection Phaseのデータ構造は可変長のデータがあまり出てこないので簡単ですね。ngrepで見ていた時はこんな感じのpayloadだったので

手で分割していくとこのようになります。

なんとなくそれっぽいデータになりましたね。とはいえ手で分割してても時間かかりますし、面白く無いのでInitial PacketをGoでparseしてみるプログラムを書いてみました。
これを元にnet.DialでつなげてみたりするとInitial Packetだけはparseできたり、無駄に頑張ればオレオレMySQL Clientが作れるので参考に遊んでみてもらえれば、と思います。

http://play.golang.org/p/Gvb4fxZETE

もし間違っていたら突っ込みいただけるとありがたいです。

そのほか

  • homebrewのmysqlはmysql.server stopで止められます
  • Character Set