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
を使ってデータベースアクセスのプログラミングを行います。XProtoco
l / 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
すれば、ロールバックされます。(当たり前のことを言っていますね。)