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 READREAD COMMITEDの違いを実際の動きで確認してみることにする。

まずは分離レベル確認。MySQLデフォルトなのでREPEATABLE READである。

1
2
3
4
5
6
7
mysql> select @@GLOBAL.tx_isolation, @@tx_isolation;
+-----------------------+-----------------+
| @@GLOBAL.tx_isolation | @@tx_isolation |
+-----------------------+-----------------+
| REPEATABLE-READ | REPEATABLE-READ |
+-----------------------+-----------------+
1 row in set (0.02 sec)

テーブルを用意する。

1
mysql> create table test(id int(8) not null primary key, name varchar(64));

テストデータ挿入。

1
mysql> insert into test values (1, 'hoge');

以降、セッションを2つ張り、2つのトランザクション処理を平行に走らせる。

REPEATABLE READの場合

現在のトランザクション分離レベルを確認。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
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つ開始。

1
2
mysql1> start transaction;
mysql2> start transaction;

トランザクション1でupdate

1
2
3
mysql1> update test set name='fuga';
Query OK, 1 row affected (0.00 sec)
Rows matched: 1 Changed: 1 Warnings: 0

トランザクション2でselect

1
2
3
4
5
6
7
mysql2> select * from test;
+----+------+
| id | name |
+----+------+
| 1 | hoge |
+----+------+
1 row in set (0.00 sec)

更新された値は見えない。

トランザクション1でcommit。

1
2
mysql1> commit;
Query OK, 0 rows affected (0.01 sec)

トランザクション2でselect。

1
2
3
4
5
6
7
mysql2> select * from test;
+----+------+
| id | name |
+----+------+
| 1 | hoge |
+----+------+
1 row in set (0.01 sec)

更新された値はやはり見えない。ここがREAD COMMITEDとの違い。READ COMMITEDではこの時点でトランザクション1での更新結果が見える。

トランザクション2を終了させてselect。

1
2
3
4
5
6
7
8
9
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の場合

分離レベル変更。

1
2
3
4
5
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)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
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つ開始。

1
2
mysql1> start transaction;
mysql2> start transaction;

トランザクション1でupdate。

1
mysql1> update test set name='fuga';

トランザクション2でselect。

1
2
3
4
5
6
7
mysql2> select * from test;
+----+------+
| id | name |
+----+------+
| 1 | hoge |
+----+------+
1 row in set (0.00 sec)

更新された値は見えない。

トランザクション1でcommit。

1
2
mysql1> commit;
Query OK, 0 rows affected (0.01 sec)

トランザクション2でselect。

1
2
3
4
5
6
7
mysql2> select * from test;
+----+------+
| id | name |
+----+------+
| 1 | fuga |
+----+------+
1 row in set (0.00 sec)

更新された値を取得できる。

Amazon Auroraの場合

長い前置きになってしまったが、Amazon Auroraの場合。

まず、デフォルトの分離レベルを確認。やはりデフォルトはREPEATABLE-READ

1
2
3
4
5
6
7
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に変更。

1
2
3
4
5
6
7
8
9
10
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にご相談下さいませ!

このエントリーをはてなブックマークに追加