tom__bo’s Blog

MySQL!! MySQL!! @tom__bo

MySQLでトランザクションの挙動を試すためのツールを作った

mytxという、トランザクション分離レベルの違いを調査したり、並列するトランザクションでどのようにロックがかけられているかを実験するためのツールを作りました。

mytxとは

MySQLで分離レベルごとの挙動を実験しようとした時に、ウィンドウを複数開いて順番にコマンド叩いていくのが面倒だなーと思ったので作りました。

github.com

gihyoの第50回 トランザクション分離レベルを試してみるでもやっているような、トランザクションごとにコネクションを張って、ウィンドウを行き来しつつコマンドを打って、場合によっては確認用のウィンドウも作ってロックの状況を確認して、とやるところを、予めSQLをファイルに用意しておくことで簡単に試せるようになります。 これくらいなら他のDBにも対応しようと思えば出来ますが、今はMySQLを対象にしています。

例えば2つのトランザクションを次のような順番で実行したいとき、

f:id:tom__bo:20171210150705p:plain

トランザクションの番号とコマンドをカンマで区切った、実行手順を書いたcsvファイル(プランファイル)を以下のように用意します。

DirtyRead.txt

1,UPDATE t1 SET c2 = 70 WHERE id = 4
2,SELECT * from t1 WHERE id = 4
1,ROLLBACK
2,COMMIT

(BEGINは不要)

これを指定してmytxを実行すると、トランザクションを分けて行ごとに実行できます。 実行中はプランファイルの行ごとに内部コマンドを受け付けて、実行したりスキップしたりといったアクションを選択出来ます。

% mytx -u root -p mysql DirtyRead.txt
stderr -> mysql: [Warning] Using a password on the command line interface can be insecure.

1: UPDATE t1 SET c2 = 70 WHERE id = 4
>
2: SELECT * from t1 WHERE id = 4
>
====================== row:  1 ======================
    id: 4
    c1: 4
    c2: 70

1: ROLLBACK
>
2: COMMIT
>

コマンドを実行すると、内部で次に実行するSQLコマンドが表示され、>が表示されて内部コマンドを待ちます。 ここで、(enter): 実行やs: スキップが出来ます。

さらにcheck用のSQLコマンドを用意しておくことで、この時のMySQLの状況を確認することも出来ます。 コマンド実行時に-cでcheck用のファイルを指定することで、インタラクティブなモードでc[n]と打った時にn+1行目のコマンドを実行できます。 これで実行中のロック状況などを確認するのが目的です。

check.txt

select * from performance_schema.data_locks d LEFT JOIN information_schema.innodb_trx i ON d.ENGINE_TRANSACTION_ID = i.trx_id;

また、実行前にテーブルの初期化や設定の変更をしたいときは設定用の.sqlファイルを用意し、-iオプションで指定することで初期化処理も出来ます。

init.sql (設定ファイル)

DROP TABLE IF EXISTS t1;

CREATE TABLE sample.t1 (
  id int AUTO_INCREMENT NOT NULL PRIMARY KEY,
  c1 int UNIQUE KEY,
  c2 int
);

INSERT INTO t1 (c1, c2) VALUES(1, 10);
INSERT INTO t1 (c1, c2) VALUES(2, 20);

初期化SQL(init.sql)とcheck用コマンド(check.txt)を指定して、実行した例。

% mytx -i init.sql -c check.sql -u root -p mysql DirtyRead.txt
stderr -> mysql: [Warning] Using a password on the command line interface can be insecure.

1: UPDATE t1 SET c2 = 70 WHERE id = 4
> 
2: SELECT * from t1 WHERE id = 4
> c
====================== row:  1 ====================== 
                    ENGINE: INNODB
            ENGINE_LOCK_ID: 12270:171
     ENGINE_TRANSACTION_ID: 12270
                 THREAD_ID: 56
            .......... (省略) ..............

====================== row:  2 ====================== 
                    ENGINE: INNODB
            ENGINE_LOCK_ID: 12270:101:4:5
     ENGINE_TRANSACTION_ID: 12270
                 THREAD_ID: 56
            .......... (省略) ..............

2: SELECT * from t1 WHERE id = 4
> 
====================== row:  1 ====================== 
    id: 4
    c1: 4
    c2: 70

1: ROLLBACK
> 
2: COMMIT
> 

これで適当なinit.sqlやcheck.txtを用意しておくことでさくさくとトランザクションの状況を確認できるようになりました。個人的には試したいトランザクションSQL群を1ファイルに用意できるようになって便利だなーと思っています。(補完とかできないけれど)