2024.04.05
TiDBのREPEATABLE READの挙動
MySQL互換のNewSQLであるTiDBのREPEATABLE READの挙動を調査しました。
1.TiDB
TiDBはMySQL互換のNewSQLです。トランザクション含めてアプリケーションからはMySQLと同等に扱うことが可能な上に,Writeがスケールアウト可能という魅力的な製品です。
見かけ上MySQLに近いTiDBですが,内部のアーキテクチャはMySQLとは別物です。MySQLは,アーキテクチャの工夫によりREPEATABLE READを実現しています。
今回は,TiDBとMySQLでREPEATABLE READの挙動に差があるのか,調査してみます。
2.トランザクション分離レベルとREPEATABLE READ
トランザクションは複数同時に実行しても違いに影響を与えないことが理想です。しかし,現実に実装をする場合,技術的な制約(または性能を出すための意図)により,別のトランザクションに影響を及ぼすことがあります。
トランザクションがお互いに影響を与える度合いを,トランザクション分離レベルと呼びます。
※ トランザクション分離レベルはこちらの記事のように素晴らしい解説が多く存在しますので,説明は割愛します。
MySQLはデフォルトでREPEATABLE READというトランザクション分離レベルになっています。REPEATABLE READは,トランザクション間の影響が実用上問題ない程度に小さくなっています。詳細は上記の記事や,Web上の解説記事を参照してください。
3.MySQLとTiDBの動作の比較
それでは,早速MySQLとTiDBのREPEATABLE READの動作を比較します。
3.1 TiDBでファジーリードが発生しない
REPEATABLE READでは,ファジーリードは発生しません。もちろん,MySQLのREPEATABLE READでもファジーリードは発生しません。TiDBでも確認してみましょう。
-- TX A mysql> begin; Query OK, 0 rows affected (0.13 sec) mysql> select * from test; +----+------+ | id | c1 | +----+------+ | 1 | 10 | | 2 | 20 | +----+------+ 2 rows in set (0.13 sec) -- TX B mysql> begin; Query OK, 0 rows affected (0.13 sec) mysql> update test set c1 = 30 where id = 1; Query OK, 1 row affected (0.11 sec) Rows matched: 1 Changed: 1 Warnings: 0 mysql> commit; -- TX A mysql> select * from test; +----+------+ | id | c1 | +----+------+ | 1 | 10 | | 2 | 20 | +----+------+ 2 rows in set (0.14 sec)
TX Aは,TX Bがコミットする前にtestを参照しているため,TX Bがtestのデータを更新してコミットした後も変更前のデータが見えています。当然ですが,ファジーリードは発生していません。
3.2 TiDBでファントムリードが発生しない
MySQLのREPEATABLE READでは,ファントムリードが発生しません。TiDBではどうでしょうか。
-- TX A mysql> begin; Query OK, 0 rows affected (0.10 sec) mysql> select * from test; +----+------+ | id | c1 | +----+------+ | 1 | 10 | | 2 | 20 | +----+------+ 2 rows in set (0.12 sec) -- TX B mysql> begin; Query OK, 0 rows affected (0.10 sec) mysql> insert into test values(3, 30); Query OK, 1 row affected (0.11 sec) mysql> select * from test; +----+------+ | id | c1 | +----+------+ | 1 | 10 | | 2 | 20 | | 3 | 30 | +----+------+ 3 rows in set (0.11 sec) mysql> commit; Query OK, 0 rows affected (0.11 sec) -- TX A mysql> select * from test; +----+------+ | id | c1 | +----+------+ | 1 | 10 | | 2 | 20 | +----+------+ 2 rows in set (0.11 sec)
TX BでINSERTしたデータは,TX Aから見えませんでした。つまり,TiDBのREPEATABLE READも,ファントムリードが発生しませんでした。このあたりの挙動はMySQLと同じなので,問題になることはなさそうです。
3.3 ギャップロック
-- TX A
mysql> begin;
Query OK, 0 rows affected (0.11 sec)
mysql> select * from test;
+----+------+
| id | c1 |
+----+------+
| 1 | 20 |
| 2 | 30 |
+----+------+
2 rows in set (0.10 sec)
mysql> select * from test where c1 = 30 for update;
+----+------+
| id | c1 |
+----+------+
| 2 | 30 |
+----+------+
1 row in set (0.11 sec)
-- MySQLなら,ここでc1の20〜30までがギャップロックされるはず
-- TX B
mysql> begin;
Query OK, 0 rows affected (0.10 sec)
mysql> insert into test values(3,25);
Query OK, 1 row affected (0.10 sec)
-- MySQLはギャップロックの解除待ちで,ここで待機が発生するはずだが,そのまま通った
mysql> commit;
Query OK, 0 rows affected (0.11 sec)
TiDBでは,MySQLと異なりINDEXのギャップに対するロックが発生しませんでした。非UNIQUEなINDEXに対するロックと,そのテーブルに対する挿入が同時に大量に走るワークロードでも,ロック待ちが発生せずに高いスループットが発揮されることが期待できます。
4.おまけ
TiDBにもInformationSchemaとPerformanceSchemaがあります。ただし,PerformanceSchemaは5.7相当なので,MySQL 8系のものに比べるとやや力不足です。とくに,近年は8系で強化されたPerformanceSchemaを使って監視・運用を行っている方が多いと思いますので,そのあたりは少々不便かもしれません。
グループ研究開発本部の最新情報をTwitterで配信中です。ぜひフォローください。
Follow @GMO_RD