📣

TiDB Cloud Serverless が
Starter
に変わりました!このページは自動翻訳されたものです。
原文はこちらからご覧ください。

TiDB 悲観的トランザクションモード

TiDB を従来のデータベースに近づけ、移行コストを削減するため、v3.0 以降では、楽観的トランザクションモデルに加えて悲観的トランザクションモードもサポートしています。このドキュメントでは、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の悲観的トランザクションと同様に動作します。1の小さな違いをご覧くださいMySQL InnoDBとの違い

  • 悲観的トランザクションの場合、TiDB はスナップショット読み取りと現在の読み取りを導入します。

    • スナップショット読み取り:トランザクション開始前にコミットされたバージョンを読み取る、ロック解除された読み取りです。1 SELECTのステートメントの読み取りはスナップショット読み取りです。
    • 現在の読み取り:コミットされた最新のバージョンを読み取るロックされた読み取りです。1、3、5、またはINSERT UPDATEステートメントの読み取りSELECT FOR UPDATE現在の読み取りDELETE

    次の例では、スナップショット読み取りと現在の読み取りについて詳しく説明します。

    セッション1セッション2セッション3
    テーブル t を作成します (INT)。
    T値に挿入(1);
    悲観的に始める。
    tを更新し、a = a + 1に設定します。
    悲観的に始める。
    SELECT * FROM t; -- スナップショット読み取りを使用して、現在のトランザクションの開始前にコミットされたバージョンを読み取ります。結果はa=1を返します。
    悲観的に始める。
    SELECT * FROM t FOR UPDATE; -- 現在の読み取りを使用します。ロックを待機します。
    COMMIT; -- ロックを解除します。セッション3のSELECT FOR UPDATE操作によってロックが取得され、TiDBは現在の読み取りを使用して最新のコミット済みバージョンを読み取ります。結果はa=2を返します。
    SELECT * FROM t; -- スナップショット読み取りを使用して、現在のトランザクションの開始前にコミットされたバージョンを読み取ります。結果はa=1を返します。
  • UPDATE 、またはINSERTステートメントを実行すると、コミットされた最新のデータが読み取られ、データが変更され、変更された行に悲観的ロックDELETE適用されます。

  • SELECT FOR UPDATEステートメントの場合、変更された行ではなく、コミットされたデータの最新バージョンに悲観的ロックが適用されます。

  • トランザクションがコミットまたはロールバックされると、ロックは解除されます。データの変更を試みる他のトランザクションはブロックされ、ロックが解除されるまで待機する必要があります。TiDBはマルチバージョン同時実行制御(MVCC)を使用しているため、データの読み取りを試みるトランザクションはブロックされません。

  • システム変数tidb_constraint_check_in_place_pessimistic設定することで、一意制約チェックを伴う悲観的ロックをスキップするかどうかを制御できます。詳細は制約参照してください。

  • 複数のトランザクションが互いのロックを取得しようとすると、デッドロックが発生します。これは自動的に検出され、いずれかのトランザクションがランダムに終了し、MySQL互換のエラーコード1213が返されます。

  • トランザクションは新しいロックを取得するために最大innodb_lock_wait_timeout秒(デフォルト:50秒)待機します。このタイムアウトに達すると、MySQL互換のエラーコード1205返されます。複数のトランザクションが同じロックを待機している場合、優先順位はトランザクションのstart tsに基づいてほぼ決定されます。

  • TiDBは、同一クラスタ内で楽観的トランザクションモードと悲観的トランザクションモードの両方をサポートします。トランザクション実行にはどちらのモードも指定できます。

  • TiDBはFOR UPDATE NOWAIT構文をサポートしており、ブロックしてロックが解放されるのを待つことはありません。代わりに、MySQL互換のエラーコード3572返されます。

  • 演算子Point GetBatch Point Getデータを読み取らない場合でも、指定された主キーまたは一意キーはロックされ、他のトランザクションが同じ主キーまたは一意キーをロックしたり、データを書き込んだりすることがブロックされます。

  • TiDBはFOR UPDATE OF TABLES構文をサポートしています。複数のテーブルを結合する文の場合、TiDBはOF TABLESのテーブルに関連付けられた行に対してのみ悲観的ロックを適用します。

MySQL InnoDBとの違い

  1. 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 が現在ギャップ ロックをサポートしていないために発生します。

  2. TiDB はSELECT LOCK IN SHARE MODEサポートしていません。

    TiDBはデフォルトではSELECT LOCK IN SHARE MODE構文をサポートしていません。3 tidb_enable_noop_functions有効にすると、TiDBはSELECT LOCK IN SHARE MODE構文と互換性を持つようになります。7 SELECT LOCK IN SHARE MODE実行すると、ロックなしの場合と同じ効果が得られるため、他のトランザクションの読み取りまたは書き込み操作がブロックされることはありません。

    v8.3.0以降、TiDBはtidb_enable_shared_lock_promotionシステム変数を使用してSELECT LOCK IN SHARE MODEステートメントによるロックの追加を有効にできるようになりました。ただし、今回追加されるロックは真の共有ロックではなく、 SELECT FOR UPDATEと整合性のある排他ロックであることに注意してください。TiDBとSELECT LOCK IN SHARE MODE構文の互換性を維持しながら、読み取り中に並行して実行される書き込みトランザクションによるデータの変更を防ぐために書き込みをブロックしたい場合は、この変数を有効にできます。この変数を有効にすると、 tidb_enable_noop_functionsが有効かどうかに関係なく、 SELECT LOCK IN SHARE MODEステートメントから有効になります。

  3. DDL により悲観的トランザクション コミットが失敗する可能性があります。

    MySQLでDDLを実行すると、実行中のトランザクションによってブロックされる可能性があります。しかし、このシナリオでは、TiDBではDDL操作がブロックされないため、悲観的トランザクションコミットが失敗しますERROR 1105 (HY000): Information schema is changed. [try again later] . TiDBはトランザクション実行中にTRUNCATE TABLE文を実行するため、 table doesn't existのエラーが発生する可能性があります。

  4. START TRANSACTION WITH CONSISTENT SNAPSHOT実行した後、MySQL は他のトランザクションで後で作成されたテーブルを読み取ることができますが、TiDB は読み取ることができません。

  5. 自動コミット トランザクションでは楽観的ロックが優先されます。

    悲観的モデルを使用する場合、自動コミットトランザクションはまず、オーバーヘッドの少ない楽観的モデルを使用してステートメントのコミットを試みます。書き込み競合が発生した場合、トランザクションの再試行には悲観的モデルが使用されます。したがって、 tidb_retry_limit 0に設定した場合、自動コミットトランザクションは書き込み競合が発生したときにWrite Conflictエラーを報告します。

    自動コミットSELECT FOR UPDATEステートメントはロックを待機しません。

  6. ステートメント内のEMBEDDED SELECTによって読み取られたデータはロックされません。

  7. TiDBでは、オープントランザクションはガベージコレクション(GC)をブロックしません。デフォルトでは、悲観的トランザクションの最大実行時間は1時間に制限されています。この制限は、TiDB設定ファイルのmax-txn-ttl[performance]を編集することで変更できます。

分離レベル

TiDB は、悲観的トランザクション モードで次の 2 つの分離レベルをサポートします。

悲観的なトランザクションコミットプロセス

トランザクションのコミットプロセスにおいて、悲観的トランザクションと楽観的トランザクションは同じロジックを持ちます。どちらのトランザクションも2相コミット(2PC)モードを採用しています。悲観的トランザクションにおける重要な適応は、DML実行です。

TiDB pessimistic transaction commit process

悲観的トランザクションは、2PCの前にフェーズAcquire Pessimistic Lock追加します。このフェーズには以下のステップが含まれます。

  1. (楽観的トランザクション モードと同じ) TiDB はクライアントからbeginリクエストを受信し、現在のタイムスタンプはこのトランザクションの start_ts になります。
  2. TiDBサーバーはクライアントから書き込み要求を受信すると、TiDBサーバーはTiKVサーバーに悲観的ロック要求を開始し、ロックは TiKVサーバーに永続化されます。
  3. (楽観的トランザクション モードと同じ) クライアントがコミット要求を送信すると、TiDB は楽観的トランザクション モードと同様に 2 フェーズ コミットの実行を開始します。

Pessimistic transactions in TiDB

パイプライン化されたロックプロセス

悲観的ロックを追加するには、TiKVへのデータ書き込みが必要です。ロック追加成功のレスポンスは、 Raftを介したコミットと適用後にのみTiDBに返されます。そのため、楽観的トランザクションと比較して、悲観的トランザクションモードでは必然的にレイテンシーが高くなります。

ロックのオーバーヘッドを削減するため、TiKVはパイプライン化されたロック処理を実装しています。データがロックの要件を満たすと、TiKVは直ちにTiDBに後続のリクエストの実行を通知し、悲観的ロックへの非同期書き込みを実行します。この処理により、レイテンシーが大幅に削減され、悲観的トランザクションのパフォーマンスが大幅に向上します。ただし、TiKVでネットワーク分断が発生した場合、またはTiKVノードがダウンした場合、悲観的ロックへの非同期書き込みが失敗し、以下の影響が生じる可能性があります。

  • 同じデータを変更する他のトランザクションをブロックすることはできません。アプリケーションロジックがロックまたはロック待機メカニズムに依存している場合、アプリケーションロジックの正確性に影響が出ます。

  • トランザクションのコミットが失敗する可能性は低いですが、トランザクションの正確性には影響しません。

アプリケーション ロジックがロックまたはロック待機メカニズムに依存している場合、または TiKV クラスターの異常が発生した場合でもトランザクション コミットの成功率を可能な限り保証したい場合は、パイプライン ロック機能をTiDB Cloudサポートにお問い合わせください無効にすることができます。

メモリ内悲観的ロック

v6.0.0では、TiKVにインメモリ悲観的ロック機能が導入されました。この機能を有効にすると、悲観的ロックは通常、リージョンリーダーのメモリにのみ保存され、ディスクに永続化されず、 Raftを介して他のレプリカに複製されることもありません。この機能により、悲観的ロックの取得にかかるオーバーヘッドが大幅に削減され、悲観的トランザクションのスループットが向上します。

インメモリ悲観的ロックのメモリ使用量がリージョンまたはTiKVノードのメモリしきい値を超えると、悲観的ロックの取得はパイプライン化されたロックプロセスに切り替わります。リージョンの統合やリーダーの交代時には、悲観的的ロックの喪失を回避するため、TiKVはインメモリ悲観的ロックをディスクに書き込み、他のレプリカに複製します。

インメモリ悲観的ロックはパイプライン化されたロック処理と同様に動作し、クラスターが正常な場合はロックの取得に影響を与えません。ただし、TiKVでネットワーク分離が発生した場合、またはTiKVノードがダウンした場合、取得済みの悲観的ロックが失われる可能性があります。

アプリケーション ロジックがロック取得またはロック待機メカニズムに依存している場合、またはクラスターが異常な状態にある場合でもトランザクション コミットの成功率を可能な限り保証する場合は、メモリ内悲観的ロック機能を無効にする必要があります。

この機能はデフォルトで有効になっています。無効にするには、TiKVの設定を変更してください。

[pessimistic-txn] in-memory = false

この機能を動的に無効にするには、TiKV 構成を動的に変更します。

set config tikv pessimistic-txn.in-memory='false';

このページは役に立ちましたか?