MySQLのXmasはXprotocolで決まり
昨日(12/24)は、協会のミサに行ってきました。牧師の説教はSNS批判でした。その話を私なりに解釈すると、「SNSで批判するときは、相手がリアルに目の前にいてもその発言ができるか、その覚悟を持った上でSNSを使え」と伝えているように感じました*1。あと、かなり蛇足ですが、聖餐を牧師が落としたのですが、すかさず拾って自分で食べて、何事もなかったようにしていたのを目撃しました*2。
X
MySQL Casual Advent Calendar 2017は、開始されてから早い時期に24枠がすぐにうまってしまいました。1,2週間ほど最後の1枠が埋まっていませんでした。様子見していたのですが、Xに引っ掛けたネタを持っていたことを思い出し、誠悦ながら参加させていただくことにしました。
さて、「Xmasですね。MySQLのXと言えば、、、」と勿体ぶったタイトルにしていますが、MySQLのXと言えばXProtocolです。
XProtocol
MySQLにはXProtocolと呼ばれるクライアントサーバ間の通信規約があります。
MySQL :: MySQL Internals Manual :: 15 X Protocol
そしてXProtocolを実装したXDevAPIが用意されています。ユーザはXDevAPIを使ってデータベースアクセスのプログラミングを行います。XProtocol / XDevAPIの特徴として、ドキュメントストアに対応していることが強調されることが多いです。例えば、次の資料があります。
https://www.db-tech-showcase.com/wp-content/dbts_museum/MySQL/MySQLDocStore_jp.pdf
検索すれば他にもたくさん情報が出てきます。しかし、XProtocolの仕様をよくよく読み込んでみると、ドキュメントストア処理以外にも、面白い仕様があるので紹介させてください。
Pipelining
Pipelineという機能です。
ざっくり言えば、クライアントの要求を、サーバからの応答を待たずに、どんどん送って処理をするという機能です。下の資料のPipeliningの節にシーケンスがあります。
MySQL :: MySQL Internals Manual :: 15.8 Implementation Notes
仕様上は、Expectationsという機構を使ってクライアントの要求を作ります。
MySQL :: MySQL Internals Manual :: 15.4 Expectations
例(エラーなし)
次のような5つのinsertを実行することを考えます。*3
insert into test_users values (1, 'mike' , 'mike@example.com' , 45) --sql1 insert into test_users values (2, 'nancy' , 'nancy@example.com' , 115) --sql2 insert into test_users values (3, 'steve' , 'steve@example.com' , 298) --sql3 insert into test_users values (4, 'james' , 'steve@example.com' , 444) --sql4 insert into test_users values (5, 'jhon' , 'steve@example.com' , 555) --sql5
通常は、
クライアントが
sqlNをサーバに送信し、クライアントはサーバからの結果を待ち、サーバから結果を受信し、クライアントは結果が正常であることを確認、を5回繰り返(N=1..5)します。
Pipelineを使うと
クライアントが
sql1からsql5をサーバに送信し、クライアントはサーバからの結果を待ち、サーバからsql1からsql5の結果を受信し、クライアントは結果に基づいて後続処理を行う
となります。
待ち時間を考慮するとPipelineのほうが全体の実行時間が短くなることが期待できます*4。
例(エラーあり)
では、クライアントからまとめて送信したデータの中にエラーがある場合はどうでしょうか。
insert into test_users values (1, 'mike' , 'mike@example.com' , 45) --sql1 insert into test_users values (2, 'nancy' , 'nancy@example.com' , 115) --sql2 insert into test_users_ values (3, 'steve' , 'steve@example.com' , 298) --sql3 テーブル名が不正 insert into test_users values (4, 'james' , 'steve@example.com' , 444) --sql4 insert into test_users values (5, 'jhon' , 'steve@example.com' , 555) --sql5
サーバ側の挙動は、クライアントでの指定により異なります。sql3で失敗した場合
no_error指定があれば、sql4,sql5は実行されない- なにも指定がなければ(または上記の
no_errorを打ち消す指定をすれば)、sql4,sql5は実行
となります。
no_errorの指定は、対象となるsql実行範囲の前後に、openとcloseのメッセージをつけ、openメッセージのcondition属性にno_errorをもつConditinoメッセージを指定します((設定が面倒ですが、openのメッセージ構造はpipeline以外の機能も指定できる仕組みになっています))。
要求メッセージのイメージは下記になります。
open{condition = Condition {condition_key = no_error}}
insert1
insert2
insert3 (不正なクエリ)
insert4
insert5
close{}
実行例
さて、実行結果を確認しましょう。Wiresharkでパケットを確認します*5。
なにも指定しない場合

- No.1からNo.6はコネクション確立のメッセージのやりとりです。
- No.7からNo.11はクライアントが要求するクエリのメッセージです。
- No.12からNo.16はサーバの応答です。
- No.14のみエラーとなり、後続の
SQLは実行されています。
- No.14のみエラーとなり、後続の
Pipelineのno_error指定をした場合

- No.1からNo.6はコネクション確立のメッセージのやりとりです。
- No.7からNo.14は基本的にクライアントが要求するクエリのメッセージです。
- No.7が
Pipelineの開始指定で、No.14がPipelineの終了指定になります。 - 途中No.10の
OKメッセージがあります。これは、Pipeline開始に対応するサーバの応答になります。
- No.7が
- No.15からNo.20はサーバの応答です。
- No.17の不正なクエリのエラーが応答されています。
- No.18からNo.20のエラーは
Pipelineとしてのエラーになります。 - 特に、4番目、5番目のクエリに対応するNo.19、No.20はクエリ自体はサーバでは実行されません。
以下にPipeline処理にて特有のメッセージ内容を個別に確認しましょう。
no_errorメッセージ(上記のNo.7)

クエリエラーのメッセージ(上記のNo.17)

Piplineエラーのメッセージ(上記のNo.20、(No.18,No.19も同様です。))

まとめ
メッセージの内容を確認しながらPipelineの基本的な挙動を見てきました。考え方自体はシンプルで、応答を待たずに要求を送り応答を受け取る仕組みです。失敗時の挙動を制御できます。例えば、失敗したら後続は実行しない。
最後に2点追加コメントになります。
ちょっとどのようなユースケースがあるかわかりませんが、仕様を読むとPipelineの単位はネストできるようです。
Pipeline自体は、トランザクションとは独立しています。トランザクションを開始した上で、Pipeline処理を行い、rollbackすれば、ロールバックされます。(当たり前のことを言っていますね。)