Amazon Auroraのトランザクション分離レベルはREPEATABLE READだけじゃないっぽい
MySQLのデフォルトのトランザクション分離レベルはREPEATABLE READ
である。ただ、最近作っていたWebアプリケーションで、READ COMMITED
にしなければいけない状況があった。現状では、Amazon RDSのMySQLを使っており、アプリケーション側でセッションごとにREAD COMMITED
に変更する対応をした。ただその一方、今後サービスの拡大にともなってAmazon Auroraへの移行も考えられるかなと思いちょこちょこと調べていた所、Amazon Auroraでは分離レベルをREPEATABLE READ
から変更できないという気になる噂があった。
It seems that the only TRANSACTION ISOLATION level supported is REPEATABLE-READ. When I try to change to SERIALIZABLE or READ-COMMITTED, Aurora accepts this without an error, but silently ignores it. tx_isolation stays REPEATABLE-READ.
Amazon Aurora – Looking Deeper
日本語訳
2015年11月16日の記事であるが、SERIALIZABLE
にもREAD-COMMITTED
にも変更できなかったとのこと。
さらには、
Actually, there I face another worrisome behaviour: silent changes in Aurora without notification.
I am pretty sure, that when a couple of weeks ago I tried to use SERIALIZABLE level, it failed with an error: “SERIALIZABLE is not supported”. Now it just silently ignores it.
その前にはまた別の動きで、SERIALIZABLE
に変更しようとした時点でエラーが出たとのこと。
気になったので、私もとりあえずREAD COMMITED
にできるかを試してみた。結論から言うと、現在は___できる___。
まずはMySQLで試してみる
MySQLで、REPEATABLE READ
とREAD COMMITED
の違いを実際の動きで確認してみることにする。
まずは分離レベル確認。MySQLデフォルトなのでREPEATABLE READ
である。
mysql> select @@GLOBAL.tx_isolation, @@tx_isolation;
+-----------------------+-----------------+
| @@GLOBAL.tx_isolation | @@tx_isolation |
+-----------------------+-----------------+
| REPEATABLE-READ | REPEATABLE-READ |
+-----------------------+-----------------+
1 row in set (0.02 sec)
テーブルを用意する。
mysql> create table test(id int(8) not null primary key, name varchar(64));
テストデータ挿入。
mysql> insert into test values (1, 'hoge');
以降、セッションを2つ張り、2つのトランザクション処理を平行に走らせる。
REPEATABLE READの場合
現在のトランザクション分離レベルを確認。
mysql1> select @@GLOBAL.tx_isolation, @@tx_isolation;
+-----------------------+-----------------+
| @@GLOBAL.tx_isolation | @@tx_isolation |
+-----------------------+-----------------+
| REPEATABLE-READ | REPEATABLE-READ |
+-----------------------+-----------------+
1 row in set (0.00 sec)
mysql2> select @@GLOBAL.tx_isolation, @@tx_isolation;
+-----------------------+-----------------+
| @@GLOBAL.tx_isolation | @@tx_isolation |
+-----------------------+-----------------+
| REPEATABLE-READ | REPEATABLE-READ |
+-----------------------+-----------------+
1 row in set (0.00 sec)
トランザクションを2つ開始。
mysql1> start transaction;
mysql2> start transaction;
トランザクション1でupdate
mysql1> update test set name='fuga';
Query OK, 1 row affected (0.00 sec)
Rows matched: 1 Changed: 1 Warnings: 0
トランザクション2でselect
mysql2> select * from test;
+----+------+
| id | name |
+----+------+
| 1 | hoge |
+----+------+
1 row in set (0.00 sec)
更新された値は見えない。
トランザクション1でcommit。
mysql1> commit;
Query OK, 0 rows affected (0.01 sec)
トランザクション2でselect。
mysql2> select * from test;
+----+------+
| id | name |
+----+------+
| 1 | hoge |
+----+------+
1 row in set (0.01 sec)
更新された値はやはり見えない。ここがREAD COMMITED
との違い。READ COMMITED
ではこの時点でトランザクション1での更新結果が見える。
トランザクション2を終了させてselect。
mysql2> commit;
Query OK, 0 rows affected (0.00 sec)
mysql2> select * from test;
+----+------+
| id | name |
+----+------+
| 1 | fuga |
+----+------+
1 row in set (0.00 sec)
更新された値が見える。
READ COMMITEDの場合
分離レベル変更。
mysql1> set session transaction isolation level read committed;
Query OK, 0 rows affected (0.00 sec)
mysql2> set session transaction isolation level read committed;
Query OK, 0 rows affected (0.00 sec)
mysql1> select @@GLOBAL.tx_isolation, @@tx_isolation;
+-----------------------+----------------+
| @@GLOBAL.tx_isolation | @@tx_isolation |
+-----------------------+----------------+
| REPEATABLE-READ | READ-COMMITTED |
+-----------------------+----------------+
1 row in set (0.00 sec)
mysql2> select @@GLOBAL.tx_isolation, @@tx_isolation;
+-----------------------+----------------+
| @@GLOBAL.tx_isolation | @@tx_isolation |
+-----------------------+----------------+
| REPEATABLE-READ | READ-COMMITTED |
+-----------------------+----------------+
1 row in set (0.00 sec)
nameをhoge
に戻しておいた上で、トランザクションを2つ開始。
mysql1> start transaction;
mysql2> start transaction;
トランザクション1でupdate。
mysql1> update test set name='fuga';
トランザクション2でselect。
mysql2> select * from test;
+----+------+
| id | name |
+----+------+
| 1 | hoge |
+----+------+
1 row in set (0.00 sec)
更新された値は見えない。
トランザクション1でcommit。
mysql1> commit;
Query OK, 0 rows affected (0.01 sec)
トランザクション2でselect。
mysql2> select * from test;
+----+------+
| id | name |
+----+------+
| 1 | fuga |
+----+------+
1 row in set (0.00 sec)
更新された値を取得できる。
Amazon Auroraの場合
長い前置きになってしまったが、Amazon Auroraの場合。
まず、デフォルトの分離レベルを確認。やはりデフォルトはREPEATABLE-READ
。
mysql> select @@GLOBAL.tx_isolation, @@tx_isolation;
+-----------------------+-----------------+
| @@GLOBAL.tx_isolation | @@tx_isolation |
+-----------------------+-----------------+
| REPEATABLE-READ | REPEATABLE-READ |
+-----------------------+-----------------+
1 row in set (0.01 sec)
トランザクション分離レベルをREAD COMMITED
に変更。
mysql> set session transaction isolation level read committed;
Query OK, 0 rows affected (0.00 sec)
mysql> select @@GLOBAL.tx_isolation, @@tx_isolation;
+-----------------------+----------------+
| @@GLOBAL.tx_isolation | @@tx_isolation |
+-----------------------+----------------+
| REPEATABLE-READ | READ-COMMITTED |
+-----------------------+----------------+
1 row in set (0.01 sec)
変更できているようである。この後、上述したMySQLと同じ手順で2つのトランザクションで実際の動きを確認したが、きちんとREAD-COMMITED
の動きになっていた。最初に紹介したブログの時点から修正が入ったのかもしれない。(ちなみに、他の分離レベルにも変更してみたが、変更できているように見えた。ただ、実際の動きは検証していない。)
ここらへんの重要な話は修正内容をアナウンスしてほしい気もするが、トランザクション分離レベルがやわい現状ではAmazon Auroraを商用サービスで使うのはちょっとまだ時期尚早かもしれない。
Amazon AuroraなどAWSを活用したシステム構築やアプリケーション開発をご検討の企業様は、是非MMMにご相談下さいませ!