Reaptable Read,MySQL8.0にて
トランザクション内でUPDATEをした後、別トランザクションでINSERTされた行が抽出されることがある。
結論:
-
REPEATABLE READ では、トランザクション開始時点で存在しなかった行は通常の SELECT では見えません。
-
しかし、UPDATE が条件に一致する新しい行を変更すると、その行が「トランザクション内で変更されたデータ」として認識され、以降の SELECT で見えるようになります。
トランザクション1
--tran1
begin;--2番目に実行する
select * from phantom_test; --3番目に実行する
select * from phantom_test; --5番目に実行する
select * from phantom_test;--7番目に実行する
update phantom_test set value = 'updated_tran1' where id = 1; --8番目に実行する
select * from phantom_test; --9番目に実行する
update phantom_test set value = 'NEW'; -- 10番目に実行する
select * from phantom_test; --11番目に実行する
commit; -- 12番目に実行する
select * from phantom_test; --13番目に実行する
トランザクション2
tran2
begin; --1番目に実行する
insert into phantom_test(value) values ('NEW'); --4番目に実行する
commit; --6番目に実行する
実行結果
ケース1 Insertされた行が更新された場合
update後にSELECTするとcommit前だが抽出される
ケース1
--トランザクション1
mysql> SELECT VERSION();
+-----------+
| VERSION() |
+-----------+
| 8.0.40 |
+-----------+
1 row in set (0.00 sec)
mysql> SELECT @@transaction_isolation;
+-------------------------+
| @@transaction_isolation |
+-------------------------+
| REPEATABLE-READ |
+-------------------------+
1 row in set (0.01 sec)
mysql> begin;
Query OK, 0 rows affected (0.03 sec)
mysql> select * from phantom_test;
+----+-----------+
| id | value |
+----+-----------+
| 1 | updated_2 |
| 2 | updated_2 |
| 8 | updated_2 |
| 9 | updated_2 |
| 10 | updated_2 |
| 11 | updated_2 |
| 12 | updated_2 |
| 13 | term2_4 |
+----+-----------+
8 rows in set (0.03 sec)
mysql> select * from phantom_test;
+----+-----------+
| id | value |
+----+-----------+
| 1 | updated_2 |
| 2 | updated_2 |
| 8 | updated_2 |
| 9 | updated_2 |
| 10 | updated_2 |
| 11 | updated_2 |
| 12 | updated_2 |
| 13 | term2_4 |
+----+-----------+
8 rows in set (0.02 sec)
mysql> select * from phantom_test;
+----+-----------+
| id | value |
+----+-----------+
| 1 | updated_2 |
| 2 | updated_2 |
| 8 | updated_2 |
| 9 | updated_2 |
| 10 | updated_2 |
| 11 | updated_2 |
| 12 | updated_2 |
| 13 | term2_4 |
+----+-----------+
8 rows in set (0.02 sec)
mysql> update phantom_test set value = 'updated_tran1' where id = 1;
Query OK, 1 row affected (0.02 sec)
Rows matched: 1 Changed: 1 Warnings: 0
mysql> select * from phantom_test;
+----+---------------+
| id | value |
+----+---------------+
| 1 | updated_tran1 |
| 2 | updated_2 |
| 8 | updated_2 |
| 9 | updated_2 |
| 10 | updated_2 |
| 11 | updated_2 |
| 12 | updated_2 |
| 13 | term2_4 |
+----+---------------+
8 rows in set (0.01 sec)
mysql> update phantom_test set value = 'updated_tran1_2';
Query OK, 9 rows affected (0.02 sec)
Rows matched: 9 Changed: 9 Warnings: 0
mysql> select * from phantom_test;
+----+-----------------+
| id | value |
+----+-----------------+
| 1 | updated_tran1_2 |
| 2 | updated_tran1_2 |
| 8 | updated_tran1_2 |
| 9 | updated_tran1_2 |
| 10 | updated_tran1_2 |
| 11 | updated_tran1_2 |
| 12 | updated_tran1_2 |
| 13 | updated_tran1_2 |
| 14 | updated_tran1_2 |
+----+-----------------+
9 rows in set (0.01 sec)
mysql> commit;
Query OK, 0 rows affected (0.03 sec)
mysql> select * from phantom_test;
+----+-----------------+
| id | value |
+----+-----------------+
| 1 | updated_tran1_2 |
| 2 | updated_tran1_2 |
| 8 | updated_tran1_2 |
| 9 | updated_tran1_2 |
| 10 | updated_tran1_2 |
| 11 | updated_tran1_2 |
| 12 | updated_tran1_2 |
| 13 | updated_tran1_2 |
| 14 | updated_tran1_2 |
+----+-----------------+
9 rows in set (0.02 sec)
トランザクション2
mysql> begin;
Query OK, 0 rows affected (0.01 sec)
mysql> insert into phantom_test(value) values ('NEW');
Query OK, 1 row affected (0.01 sec)
mysql> commit;
Query OK, 0 rows affected (0.02 sec)
ケース2 Insertされた行が更新後の値が同じため更新されない場合
この時はupdate後にSELECTしてもcommit前は抽出されない
トランザクション1
mysql> begin;
Query OK, 0 rows affected (0.01 sec)
mysql> select * from phantom_test;
+----+-----------------+
| id | value |
+----+-----------------+
| 1 | updated_tran1_2 |
| 2 | updated_tran1_2 |
| 8 | updated_tran1_2 |
| 9 | updated_tran1_2 |
| 10 | updated_tran1_2 |
| 11 | updated_tran1_2 |
| 12 | updated_tran1_2 |
| 13 | updated_tran1_2 |
| 14 | updated_tran1_2 |
+----+-----------------+
9 rows in set (0.02 sec)
mysql> select * from phantom_test;
+----+-----------------+
| id | value |
+----+-----------------+
| 1 | updated_tran1_2 |
| 2 | updated_tran1_2 |
| 8 | updated_tran1_2 |
| 9 | updated_tran1_2 |
| 10 | updated_tran1_2 |
| 11 | updated_tran1_2 |
| 12 | updated_tran1_2 |
| 13 | updated_tran1_2 |
| 14 | updated_tran1_2 |
+----+-----------------+
9 rows in set (0.02 sec)
mysql> select * from phantom_test;
+----+-----------------+
| id | value |
+----+-----------------+
| 1 | updated_tran1_2 |
| 2 | updated_tran1_2 |
| 8 | updated_tran1_2 |
| 9 | updated_tran1_2 |
| 10 | updated_tran1_2 |
| 11 | updated_tran1_2 |
| 12 | updated_tran1_2 |
| 13 | updated_tran1_2 |
| 14 | updated_tran1_2 |
+----+-----------------+
9 rows in set (0.01 sec)
mysql> update phantom_test set value = 'updated_tran1' where id = 1;
Query OK, 1 row affected (0.01 sec)
Rows matched: 1 Changed: 1 Warnings: 0
mysql> select * from phantom_test;
+----+-----------------+
| id | value |
+----+-----------------+
| 1 | updated_tran1 |
| 2 | updated_tran1_2 |
| 8 | updated_tran1_2 |
| 9 | updated_tran1_2 |
| 10 | updated_tran1_2 |
| 11 | updated_tran1_2 |
| 12 | updated_tran1_2 |
| 13 | updated_tran1_2 |
| 14 | updated_tran1_2 |
+----+-----------------+
9 rows in set (0.01 sec)
mysql> update phantom_test set value = 'NEW';
Query OK, 9 rows affected (0.01 sec)
Rows matched: 10 Changed: 9 Warnings: 0
mysql> select * from phantom_test;
+----+-------+
| id | value |
+----+-------+
| 1 | NEW |
| 2 | NEW |
| 8 | NEW |
| 9 | NEW |
| 10 | NEW |
| 11 | NEW |
| 12 | NEW |
| 13 | NEW |
| 14 | NEW |
+----+-------+
9 rows in set (0.01 sec)
mysql> commit;
Query OK, 0 rows affected (0.02 sec)
mysql> select * from phantom_test;
+----+-------+
| id | value |
+----+-------+
| 1 | NEW |
| 2 | NEW |
| 8 | NEW |
| 9 | NEW |
| 10 | NEW |
| 11 | NEW |
| 12 | NEW |
| 13 | NEW |
| 14 | NEW |
| 15 | NEW |
+----+-------+
10 rows in set (0.01 sec)
トランザクション2
mysql> begin;
Query OK, 0 rows affected (0.00 sec)
mysql>
mysql> insert into phantom_test(value) values ('NEW');
Query OK, 1 row affected (0.01 sec)
mysql> commit;
Query OK, 0 rows affected (0.02 sec)