ドキュメント
英語読めないから5.1の日本語ドキュメントも併用して読んでいる。
検証環境
- MySQL 5.6.22
- REPEATABLE-READ
使い方
ゆるふわWebアプリケーションエンジニアなもんで、幸か不幸か今までのエンジニア人生で複数DB環境(シャーディング/マスタDB分散)に遭遇したことがない。なのでXAの使い方をドキュメントから読み取っていく。
XAトランザクションの流れ
普通のトランザクションではBEGINで始めるが、XAトランザクションの場合は
mysql> XA START 'xid';
で始める。
で、このxidってやつは何なのかというと、上記ドキュメントでは
xid: gtrid [, bqual [, formatID ]]
とあり、gtridっていうのが必須らしい。グローバルトランザクション識別子というやつなのだけれど、かぶらなければ適当にクライアントで生成してもよいっぽい? uuidでも突っ込んでおくのかな。
とりあえずサンプルでは'xatest'なんていう超適当っぽい文字列を突っ込んでいるのでそんな感じでよしなにやってく。
さてXA START 'xid'
した状態はそのトランザクションはACTIVE
と呼ばれる状態になる。
mysql> XA START 'xatest1'; Query OK, 0 rows affected (0.00 sec)
そこからSELECTだのUPDATEだのINSERTだののSQLを発行しXA END 'xid'
なるSQLを発行する。
mysql> INSERT INTO user (name, status, created_at, updated_at) VALUES ("macopy", 1, NOW(), NOW()); Query OK, 1 row affected (0.00 sec) mysql> XA END 'xatest1'; │ Query OK, 0 rows affected (0.00 sec)
するとトランザクションはIDLE
と呼ばれる状態になる。
ただしこの時点ではまだ別のトランザクションからは変更が行われていない。そうなるにはここからまだ2段階ほどステップを踏む必要がある。
XA PREAPRE 'xid'
というSQLを発行する。すると、PREPARE
という状態になる。
mysql> XA PREPARE 'xatest1'; │ Query OK, 0 rows affected (0.00 sec)
すると別のトランザクションからもこのトランザクションは反映待ちというような感じで見ることが出来る。これにはXA RECOVER
というSQLを用いる。
mysql> XA RECOVER; +----------+--------------+--------------+---------+ | formatID | gtrid_length | bqual_length | data | +----------+--------------+--------------+---------+ | 1 | 7 | 0 | xatest1 | +----------+--------------+--------------+---------+ 1 row in set (0.00 sec)
ただしまだこの状態ではコミットは行われていない。仕上げにXA COMMIT 'xid'
とする。
mysql> XA COMMIT 'xatest1';
XA RECOVER;
のリストからは消えて、トランザクション内で行われたデータベースに対する変更がコミットされる。これでトランザクションは終わりだ。
疑問点いろいろ
XAトランザクション中の制限
まずXAトランザクション内でBEGINしようとしてみる。
mysql> XA START 'xatest1'; Query OK, 0 rows affected (0.00 sec) mysql> BEGIN; ERROR 1399 (XAE07): XAER_RMFAIL: The command cannot be executed when global transaction is in the ACTIVE state
はい、ごめんなさいという感じである。
じゃあBEGINってどこまで出来ないのかというと、結局XA {COMMIT|ROLLBACK} 'xid'
するまで無理なようである。
mysql> XA END 'xatest1'; Query OK, 0 rows affected (0.05 sec) mysql> BEGIN; ERROR 1399 (XAE07): XAER_RMFAIL: The command cannot be executed when global transaction is in the IDLE state mysql> XA PREPARE 'xatest1'; Query OK, 0 rows affected (0.00 sec) mysql> BEGIN; ERROR 1399 (XAE07): XAER_RMFAIL: The command cannot be executed when global transaction is in the PREPARED state mysql> XA COMMIT 'xatest1'; Query OK, 0 rows affected (0.00 sec) mysql> BEGIN; Query OK, 0 rows affected (0.00 sec)
なお、XA ROLLBACK
とは普通のトランザクションで言うROLLBACK
のことなんですけれど、こいつはACTIVE
では発行できない。XA END 'xid'
でIDLE
にしてトランザクションから一旦抜けてからでないと出来ない。
また、ACTIVE
だと「暗黙のコミットを引き起こすステートメント」は実行できないとのこと。なにそれってなりますが、主に我々に関係するのはDDLであったりTRUNCATE文です。
mysql> XA START 'xatest1'; Query OK, 0 rows affected (0.00 sec) mysql> CREATE TABLE t1 (id INTEGER UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY); ERROR 1399 (XAE07): XAER_RMFAIL: The command cannot be executed when global transaction is in the ACTIVE state
いわゆるMySQLにおけるトランザクション内に入れても即コミットされちゃうやつ。
あとそれから、普通のトランザクションを始めた後にXAを始めようとするのも怒られる。
mysql> BEGIN; Query OK, 0 rows affected (0.00 sec) mysql> XA START 'xatest2'; ERROR 1400 (XAE09): XAER_OUTSIDE: Some work is done outside global transaction
なお、他のコネクションもXAを始めた後はXAではないといけないのではと思いましたが、そんなことはありませんでした。
PKでぶつからせる
以下の例ではコネクションの区別をTA/TBとして2つのコネクションがあるケースを扱っていく。
mysql> DESC t1; +-------+------------------+------+-----+---------+----------------+ | Field | Type | Null | Key | Default | Extra | +-------+------------------+------+-----+---------+----------------+ | id | int(10) unsigned | NO | PRI | NULL | auto_increment | +-------+------------------+------+-----+---------+----------------+ 1 row in set (0.03 sec)
以下は時系列順
TA> XA START 'xatest1'; Query OK, 0 rows affected (0.00 sec) TB> XA START 'xatest2'; Query OK, 0 rows affected (0.00 sec) TA> INSERT INTO t1 (id) VALUES (1); Query OK, 1 row affected (0.02 sec) TB> INSERT INTO t1 (id) VALUES (1); # ブロックされる TA> XA END 'xatest1'; Query OK, 0 rows affected (0.00 sec) TA> XA PREPARE 'xatest1'; Query OK, 0 rows affected (0.00 sec) TA> XA COMMIT 'xatest1'; Query OK, 0 rows affected (0.00 sec) TB> ERROR 1062 (23000): Duplicate entry '1' for key 'PRIMARY' # ブロックされていたのがここで解放
とまあXA COMMIT
するまで待たされる。普通のトランザクションと同じような挙動である。
なお、ROLLBACK時の挙動はROLLBACKした瞬間に開放される。IDLEになったあとはいつでもROLLBACK可能なので、まあそうなるか。
さてここまでは普通のトランザクションがめんどくさくなっただけな感じだったが、次からはデッドロックとかその辺りの挙動を見て行きたい。普通のトランザクションとどう違うのかが知りたい。