TiDB悲観的トランザクションモード
TiDBの使用法を従来のデータベースに近づけ、移行のコストを削減するために、v3.0以降、TiDBは楽観的なトランザクションモデルに加えて悲観的なトランザクションモードをサポートしています。このドキュメントでは、TiDBペシミスティックトランザクションモードの機能について説明します。
ノート:
v3.0.8以降、新しく作成されたTiDBクラスターは、デフォルトでペシミスティックトランザクションモードを使用します。ただし、v3.0.7以前からv3.0.8以降にアップグレードした場合、これは既存のクラスタには影響しません。つまり、新しく作成されたクラスターのみがデフォルトで悲観的トランザクションモードを使用します。
トランザクションモードの切り替え
tidb_txn_mode
のシステム変数を構成することにより、トランザクションモードを設定できます。次のコマンドは、クラスタに新しく作成されたセッションによって実行されるすべての明示的なトランザクション(つまり、非自動コミットトランザクション)をペシミスティックトランザクションモードに設定します。
SET GLOBAL tidb_txn_mode = 'pessimistic';
次のSQLステートメントを実行して、ペシミスティックトランザクションモードを明示的に有効にすることもできます。
BEGIN PESSIMISTIC;
BEGIN /*T! PESSIMISTIC */;
BEGIN PESSIMISTIC;
およびBEGIN OPTIMISTIC;
ステートメントは、 tidb_txn_mode
システム変数よりも優先されます。これらの2つのステートメントで開始されたトランザクションは、システム変数を無視し、悲観的および楽観的なトランザクションモードの両方の使用をサポートします。
行動
TiDBのペシミスティックトランザクションは、MySQLのトランザクションと同様に動作します。 MySQLInnoDBとの違いの小さな違いを参照してください。
悲観的なトランザクションの場合、TiDBはスナップショット読み取りと現在の読み取りを導入します。
- スナップショット読み取り:トランザクションが開始する前にコミットされたバージョンを読み取る、ロックされていない読み取りです。
SELECT
ステートメントの読み取りはスナップショット読み取りです。 - 現在の読み取り:最新のコミットされたバージョンを読み取るのはロックされた読み取りです。
UPDATE
、またはINSERT
ステートメントのSELECT FOR UPDATE
は現在の読み取りDELETE
。
次の例は、スナップショット読み取りと現在の読み取りの詳細な説明を提供します。
セッション1 セッション2 セッション3 CREATE TABLE t(INT); INSERT INTO T VALUES(1); 悲観的に始める; UPDATE t SET a = a + 1; 悲観的に始める; SELECT * FROM t; -スナップショット読み取りを使用して、現在のトランザクションが開始する前にコミットされたバージョンを読み取ります。結果はa=1を返します。 悲観的に始める; SELECT * FROM t FOR UPDATE; -現在の読み取りを使用します。ロックを待ちます。 専念; -ロックを解除します。セッション3のSELECTFORUPDATE操作はロックを取得し、TiDBは現在の読み取りを使用して最新のコミットされたバージョンを読み取ります。結果はa=2を返します。 SELECT * FROM t; -スナップショット読み取りを使用して、現在のトランザクションが開始する前にコミットされたバージョンを読み取ります。結果はa=1を返します。 - スナップショット読み取り:トランザクションが開始する前にコミットされたバージョンを読み取る、ロックされていない読み取りです。
UPDATE
、またはDELETE
ステートメントを実行すると、最新のコミットされたデータが読み取られ、データが変更され、変更された行にペシミスティックロックが適用されINSERT
。SELECT FOR UPDATE
ステートメントの場合、ペシミスティックロックは、変更された行ではなく、コミットされたデータの最新バージョンに適用されます。トランザクションがコミットまたはロールバックされると、ロックが解除されます。データを変更しようとする他のトランザクションはブロックされ、ロックが解除されるのを待つ必要があります。 TiDBはマルチバージョン同時実行制御(MVCC)を使用しているため、データを読み取ろうとするトランザクションはブロックされません。
複数のトランザクションが互いのそれぞれのロックを取得しようとすると、デッドロックが発生します。これは自動的に検出され、トランザクションの1つがランダムに終了し、MySQL互換のエラーコード
1213
が返されます。トランザクションは、新しいロックを取得するために最大
innodb_lock_wait_timeout
秒(デフォルト:50)待機します。このタイムアウトに達すると、MySQL互換のエラーコード1205
が返されます。複数のトランザクションが同じロックを待機している場合、優先順位はトランザクションのstart ts
にほぼ基づいています。TiDBは、同じクラスタで楽観的トランザクションモードと悲観的トランザクションモードの両方をサポートします。トランザクション実行にはどちらのモードも指定できます。
TiDBは
FOR UPDATE NOWAIT
構文をサポートし、ロックが解放されるのをブロックして待機しません。代わりに、MySQL互換のエラーコード3572
が返されます。Point Get
とBatch Point Get
の演算子がデータを読み取らない場合でも、指定された主キーまたは一意のキーをロックします。これにより、他のトランザクションが同じ主キーまたは一意のキーにデータをロックまたは書き込むことができなくなります。TiDBは
FOR UPDATE OF TABLES
構文をサポートしています。複数のテーブルを結合するステートメントの場合、TiDBはOF TABLES
のテーブルに関連付けられている行にのみペシミスティックロックを適用します。
MySQLInnoDBとの違い
TiDBがWHERE句で範囲を使用するDMLまたは
SELECT FOR UPDATE
ステートメントを実行する場合、範囲内の同時DMLステートメントはブロックされません。例えば:
CREATE TABLE t1 ( id INT NOT NULL PRIMARY KEY, pad1 VARCHAR(100) ); INSERT INTO t1 (id) VALUES (1),(5),(10);BEGIN /*T! PESSIMISTIC */; SELECT * FROM t1 WHERE id BETWEEN 1 AND 10 FOR UPDATE;BEGIN /*T! PESSIMISTIC */; INSERT INTO t1 (id) VALUES (6); -- blocks only in MySQL UPDATE t1 SET pad1='new value' WHERE id = 5; -- blocks waiting in both MySQL and TiDBこの動作は、TiDBが現在ギャップロックをサポートしていないためです。
TiDBは
SELECT LOCK IN SHARE MODE
をサポートしていません。SELECT LOCK IN SHARE MODE
を実行すると、ロックなしの場合と同じ効果があるため、他のトランザクションの読み取りまたは書き込み操作がブロックされることはありません。DDLを使用すると、悲観的なトランザクションのコミットが失敗する可能性があります。
MySQLでDDLを実行すると、実行中のトランザクションによってDDLがブロックされる可能性があります。ただし、このシナリオでは、DDL操作はTiDBでブロックされないため、悲観的なトランザクションコミットの失敗につながります:
ERROR 1105 (HY000): Information schema is changed. [try again later]
。 TiDBは、トランザクションの実行中にTRUNCATE TABLE
ステートメントを実行するため、table doesn't exist
エラーが発生する可能性があります。START TRANSACTION WITH CONSISTENT SNAPSHOT
を実行した後でも、MySQLは他のトランザクションで後で作成されるテーブルを読み取ることができますが、TiDBは読み取ることができません。自動コミットトランザクションは、楽観的ロックを優先します。
悲観的モデルを使用する場合、自動コミットトランザクションは、最初に、オーバーヘッドの少ない楽観的モデルを使用してステートメントをコミットしようとします。書き込みの競合が発生した場合、ペシミスティックモデルがトランザクションの再試行に使用されます。したがって、
tidb_retry_limit
が0
に設定されている場合でも、書き込みの競合が発生すると、自動コミットトランザクションはWrite Conflict
エラーを報告します。SELECT FOR UPDATE
ステートメントはロックを待機しません。ステートメントで
EMBEDDED SELECT
によって読み取られたデータはロックされていません。TiDBで開いているトランザクションは、ガベージコレクション(GC)をブロックしません。デフォルトでは、これによりペシミスティックトランザクションの最大実行時間が1時間に制限されます。この制限は、TiDB構成ファイルの
[performance]
の下max-txn-ttl
を編集することで変更できます。
分離レベル
TiDBは、ペシミスティックトランザクションモードで次の2つの分離レベルをサポートします。
デフォルトでは繰り返し読み取りこれはMySQLと同じです。
ノート:
この分離レベルでは、DML操作は最新のコミットされたデータに基づいて実行されます。動作はMySQLと同じですが、TiDBの楽観的なトランザクションモードとは異なります。 TiDBとMySQLの繰り返し可能な読み取りの違いを参照してください。
コミット済みを読む 。この分離レベルは、
SET TRANSACTION
ステートメントを使用して設定できます。
パイプラインロックプロセス
ペシミスティックロックを追加するには、TiKVにデータを書き込む必要があります。ロックを正常に追加した場合の応答は、コミットしてRaftを介して適用した後にのみTiDBに返すことができます。したがって、楽観的なトランザクションと比較して、悲観的なトランザクションモードは必然的に待ち時間が長くなります。
ロックのオーバーヘッドを削減するために、TiKVはパイプラインロックプロセスを実装します。データがロックの要件を満たすと、TiKVはすぐにTiDBに後続の要求を実行するよう通知し、ペシミスティックロックに非同期で書き込みます。このプロセスにより、ほとんどの遅延が減少し、悲観的なトランザクションのパフォーマンスが大幅に向上します。ただし、ネットワークパーティションがTiKVで発生するか、TiKVノードがダウンしている場合、ペシミスティックロックへの非同期書き込みが失敗し、次の側面に影響を与える可能性があります。
同じデータを変更する他のトランザクションをブロックすることはできません。アプリケーションロジックがロックまたはロック待機メカニズムに依存している場合、アプリケーションロジックの正確性が影響を受けます。
トランザクションのコミットが失敗する可能性は低いですが、トランザクションの正確性には影響しません。
アプリケーションロジックがロックまたはロック待機メカニズムに依存している場合、またはTiKVクラスタの異常の場合でもトランザクションコミットの成功率を可能な限り保証したい場合は、パイプラインロック機能を無効にする必要があります。
この機能はデフォルトで有効になっています。これを無効にするには、TiKV構成を変更します。
[pessimistic-txn]
pipelined = false
TiKVクラスタがv4.0.9以降の場合、この機能を動的に無効にすることもできTiKV構成をオンラインで変更する 。
set config tikv pessimistic-txn.pipelined='false';