June 25, 2018

proxysqlのクエリログのバイナリを読む

proxysqlは通過したクエリをログとして残すことができる。
- Query Logging · sysown/proxysql Wiki

このログの実体はバイナリで、 proxysql に付属している tools/eventslog_reader_sample.cpp で読むことができる。

$ file queries.log
queries.log: data
$ tools/eventslog_reader_sample /path/to/queris.log
ProxySQL LOG QUERY: thread_id="2" username="root" schemaname=sampledb1" client="127.0.0.1:46392" HID=0 server="127.0.0.1:3306" starttime="2018-06-25 14:37:05.165436" endtime="2018-06-25 14:37:05.166352" duration=916us digest="0x904D3DF02033E45"
show databases
ProxySQL LOG QUERY: thread_id="2" username="root" schemaname=sampledb1" client="127.0.0.1:46392" HID=0 server="127.0.0.1:3306" starttime="2018-06-25 14:37:05.166608" endtime="2018-06-25 14:37:05.166915" duration=307us digest="0xF718C50199531AEF"
show tables
ProxySQL LOG QUERY: thread_id="2" username="root" schemaname=sampledb1" client="127.0.0.1:46392" HID=NULL  starttime="2018-06-25 14:37:05.168813" endtime="2018-06-25 14:37:05.168813" duration=0us digest="0x52A2BA0B226CD90D"
select @@version_comment limit 1
ProxySQL LOG QUERY: thread_id="2" username="root" schemaname=sampledb1" client="127.0.0.1:46392" HID=0 server="127.0.0.1:3306" starttime="2018-06-25 14:37:12.841241" endtime="2018-06-25 14:37:12.841584" duration=343us digest="0x5FA7EBD64764419D"
SELECT * FROM user

このログファイルであるバイナリはどういったものなのか気になったので目で読んでいったのでメモ。


フォーマット

↑のログの最後のクエリである SELECT * from user のところを抜き出すと以下のようなバイナリとなっている。

$ xxd -s 312 queries.log
00000138: 0000 0204 726f 6f74 0973 616d 706c 6564  ....root.sampled
00000148: 6231 0f31 3237 2e30 2e30 2e31 3a34 3633  b1.127.0.0.1:463
00000158: 3932 000e 3132 372e 302e 302e 313a 3333  92..127.0.0.1:33
00000168: 3036 fe19 8c36 5178 6f05 00fe 708d 3651  06...6Qxo...p.6Q
00000178: 786f 0500 fed6 eba7 5f9d 4164 4712 5345  xo......_.AdG.SE
00000188: 4c45 4354 202a 2046 524f 4d20 7573 6572  LECT * FROM user

もう既にパッと見た感じ自明という感じがするが気にしない。
ちなみにチート技として、 https://github.com/sysown/proxysql/blob/v1.4.10/tools/eventslog_reader_sample.cpp#L75 を見るとだいたい分かる。

まず最初の行 04 726f 6f74 はユーザー名のバイト数 + ユーザー名。

続く 0973 616d 706c 6564 6231 はデータベース名のバイト数 + データベース名。

次はIPアドレスとポート情報。
0f31 3237 2e30 2e30 2e31 3a34 3633 3932
これも127.0.0.1:46392のバイト数(15byte) + IPアドレス:ポート番号。

続いて00でpaddingを挟んで接続先のMySQLのIPアドレスとポート番号。 0e 3132 372e 302e 302e 313a 3333 3036
これも同様に127.0.0.1:3306のバイト数(14byte) + IPアドレス:ポート番号。

続いて、クエリの開始時刻と終了時刻。
クエリの開始時刻の部分は 198c 3651 786f 0500
クエリの終了時刻の部分は 708d 3651 786f 0500

続いてクエリのdigest(ハッシュ値)。
d6eb a75f 9d41 6447 の部分。
今回のクエリ( SELECT * FROM user )のdigestは 0x5FA7EBD64764419D

これはSpookyHashと呼ばれる、暗号学的ハッシュ関数としての性質を持たないハッシュ関数で算出している。

「暗号学的ハッシュ関数としての性質を持たない」というのは

  • 原像計算困難性
  • 弱衝突耐性
  • 強衝突耐性

を満たさないということである。
これらの性質を持たない代わりに計算が早い。
なので大規模データの処理に向いている。

ちなみにproxysqlで使用されているSpooky Hashのビット数は64bit。Pythonだと以下のような感じになる。

from spooky import hash64
from binascii import hexlify

hexlify(hash64("SELECT * FROM user").to_bytes(8, 'little'))
# b'd6eba75f9d416447'

最後に 12 5345 4c45 4354 202a 2046 524f 4d20 7573 6572
クエリ長(18byte) + クエリ(SELECT * FROM user)。

おしまい。

まとめ

これで tools/eventslog_reader_sample.cpp が消失した世界に行っても読めるようになりました。よかったですね。

このエントリーをはてなブックマークに追加

© Kouhei Morita 2018