トランザクション
TiDBは、 悲観的つまたは楽観的のトランザクションモードを使用した分散トランザクションをサポートします。 TiDB 3.0.8以降、TiDBはデフォルトで悲観的トランザクションモードを使用します。
このドキュメントでは、一般的に使用されるトランザクション関連のステートメント、明示的および暗黙的なトランザクション、分離レベル、制約の遅延チェック、およびトランザクションサイズを紹介します。
一般的な変数には、 autocommit 、およびtidb_disable_txn_auto_retryがtidb_txn_modeれtidb_retry_limit 。
ノート:
tidb_disable_txn_auto_retry変数とtidb_retry_limit変数は楽観的なトランザクションにのみ適用され、悲観的なトランザクションには適用されません。
一般的なステートメント
トランザクションの開始
ステートメントBEGINとSTART TRANSACTIONは、新しいトランザクションを明示的に開始するために交換可能に使用できます。
構文:
BEGIN;
START TRANSACTION;
START TRANSACTION WITH CONSISTENT SNAPSHOT;
START TRANSACTION WITH CAUSAL CONSISTENCY ONLY;
これらのステートメントのいずれかが実行されたときに現在のセッションがトランザクションの処理中である場合、TiDBは新しいトランザクションを開始する前に現在のトランザクションを自動的にコミットします。
ノート:
MySQLとは異なり、TiDBは上記のステートメントを実行した後、現在のデータベースのスナップショットを取得します。 MySQLの
BEGINとSTART TRANSACTIONは、トランザクションの開始後にInnoDBからデータを読み取る最初のSELECTのステートメント(SELECT FOR UPDATEではない)を実行した後にスナップショットを取得します。START TRANSACTION WITH CONSISTENT SNAPSHOTは、ステートメントの実行中にスナップショットを取得します。その結果、BEGIN、およびSTART TRANSACTIONは、START TRANSACTION WITH CONSISTENT SNAPSHOTSTART TRANSACTION WITH CONSISTENT SNAPSHOT相当します。
トランザクションのコミット
ステートメントCOMMITは、現在のトランザクションで行われたすべての変更を適用するようにTiDBに指示します。
構文:
COMMIT;
ヒント:
楽観的なトランザクションを有効にする前に、アプリケーションが
COMMITステートメントがエラーを返す可能性があることを正しく処理していることを確認してください。アプリケーションがこれをどのように処理するかわからない場合は、代わりにデフォルトの悲観的なトランザクションを使用することをお勧めします。
トランザクションのロールバック
ステートメントROLLBACKはロールバックし、現在のトランザクションのすべての変更をキャンセルします。
構文:
ROLLBACK;
クライアント接続が中止または閉じられた場合も、トランザクションは自動的にロールバックされます。
自動コミット
MySQLの互換性に必要な場合、TiDBはデフォルトで、実行直後にステートメントを自動コミットします。
例えば:
mysql> CREATE TABLE t1 (
-> id INT NOT NULL PRIMARY KEY auto_increment,
-> pad1 VARCHAR(100)
-> );
Query OK, 0 rows affected (0.09 sec)
mysql> SELECT @@autocommit;
+--------------+
| @@autocommit |
+--------------+
| 1 |
+--------------+
1 row in set (0.00 sec)
mysql> INSERT INTO t1 VALUES (1, 'test');
Query OK, 1 row affected (0.02 sec)
mysql> ROLLBACK;
Query OK, 0 rows affected (0.01 sec)
mysql> SELECT * FROM t1;
+----+------+
| id | pad1 |
+----+------+
| 1 | test |
+----+------+
1 row in set (0.00 sec)
上記の例では、 ROLLBACKステートメントは効果がありません。これは、 INSERTステートメントが自動コミットで実行されるためです。つまり、次の単一ステートメントトランザクションと同等でした。
START TRANSACTION;
INSERT INTO t1 VALUES (1, 'test');
COMMIT;
トランザクションが明示的に開始されている場合、自動コミットは適用されません。次の例では、 ROLLBACKステートメントがINSERTステートメントを正常に元に戻します。
mysql> CREATE TABLE t2 (
-> id INT NOT NULL PRIMARY KEY auto_increment,
-> pad1 VARCHAR(100)
-> );
Query OK, 0 rows affected (0.10 sec)
mysql> SELECT @@autocommit;
+--------------+
| @@autocommit |
+--------------+
| 1 |
+--------------+
1 row in set (0.00 sec)
mysql> START TRANSACTION;
Query OK, 0 rows affected (0.00 sec)
mysql> INSERT INTO t2 VALUES (1, 'test');
Query OK, 1 row affected (0.02 sec)
mysql> ROLLBACK;
Query OK, 0 rows affected (0.00 sec)
mysql> SELECT * FROM t2;
Empty set (0.00 sec)
グローバルベースまたはセッションベースのいずれかでautocommitのシステム変数変更可能 。
例えば:
SET autocommit = 0;
SET GLOBAL autocommit = 0;
明示的および暗黙的なトランザクション
ノート:
一部のステートメントは暗黙的にコミットされます。たとえば、
[BEGIN|START TRANSACTION]を実行すると、最後のトランザクションが暗黙的にコミットされ、新しいトランザクションが開始されます。この動作は、MySQLとの互換性のために必要です。詳細については、 暗黙のコミットを参照してください。
TiDBは、明示的なトランザクション( [BEGIN|START TRANSACTION]とCOMMITを使用してトランザクションの開始と終了を定義)と暗黙的なトランザクション( SET autocommit = 1 )をサポートします。
autocommitから1の値を設定し、 [BEGIN|START TRANSACTION]ステートメントを使用して新しいトランザクションを開始すると、 COMMITまたはROLLBACKの前に自動コミットが無効になり、トランザクションが明示的になります。
DDLステートメントの場合、トランザクションは自動的にコミットされ、ロールバックをサポートしません。現在のセッションがトランザクションの処理中にDDLステートメントを実行すると、現在のトランザクションがコミットされた後にDDLステートメントが実行されます。
制約のレイジーチェック
デフォルトでは、楽観的なトランザクションは、DMLステートメントが実行されるときに主キーまたは固有の制約をチェックしません。これらのチェックは、代わりにトランザクションCOMMITで実行されます。
例えば:
CREATE TABLE t1 (id INT NOT NULL PRIMARY KEY);
INSERT INTO t1 VALUES (1);
BEGIN OPTIMISTIC;
INSERT INTO t1 VALUES (1); -- MySQL returns an error; TiDB returns success.
INSERT INTO t1 VALUES (2);
COMMIT; -- It is successfully committed in MySQL; TiDB returns an error and the transaction rolls back.
SELECT * FROM t1; -- MySQL returns 1 2; TiDB returns 1.
mysql> CREATE TABLE t1 (id INT NOT NULL PRIMARY KEY);
Query OK, 0 rows affected (0.10 sec)
mysql> INSERT INTO t1 VALUES (1);
Query OK, 1 row affected (0.02 sec)
mysql> BEGIN OPTIMISTIC;
Query OK, 0 rows affected (0.00 sec)
mysql> INSERT INTO t1 VALUES (1); -- MySQL returns an error; TiDB returns success.
Query OK, 1 row affected (0.00 sec)
mysql> INSERT INTO t1 VALUES (2);
Query OK, 1 row affected (0.00 sec)
mysql> COMMIT; -- It is successfully committed in MySQL; TiDB returns an error and the transaction rolls back.
ERROR 1062 (23000): Duplicate entry '1' for key 'PRIMARY'
mysql> SELECT * FROM t1; -- MySQL returns 1 2; TiDB returns 1.
+----+
| id |
+----+
| 1 |
+----+
1 row in set (0.01 sec)
レイジーチェックの最適化は、制約チェックをバッチ処理し、ネットワーク通信を減らすことでパフォーマンスを向上させます。 tidb_constraint_check_in_place=TRUEを設定すると、この動作を無効にできます。
ノート:
- この最適化は、楽観的なトランザクションにのみ適用されます。
- この最適化は、
INSERT IGNOREとINSERT ON DUPLICATE KEY UPDATEでは有効になりませんが、通常のINSERTのステートメントでのみ有効になります。
ステートメントのロールバック
TiDBは、ステートメントの実行が失敗した後のアトミックロールバックをサポートします。ステートメントにエラーが発生した場合、ステートメントが行った変更は有効になりません。トランザクションは開いたままになり、 COMMITまたはROLLBACKステートメントを発行する前に追加の変更を行うことができます。
CREATE TABLE test (id INT NOT NULL PRIMARY KEY);
BEGIN;
INSERT INTO test VALUES (1);
INSERT INTO tset VALUES (2); -- Statement does not take effect because "test" is misspelled as "tset".
INSERT INTO test VALUES (1),(2); -- Entire statement does not take effect because it violates a PRIMARY KEY constraint
INSERT INTO test VALUES (3);
COMMIT;
SELECT * FROM test;
mysql> CREATE TABLE test (id INT NOT NULL PRIMARY KEY);
Query OK, 0 rows affected (0.09 sec)
mysql> BEGIN;
Query OK, 0 rows affected (0.00 sec)
mysql> INSERT INTO test VALUES (1);
Query OK, 1 row affected (0.02 sec)
mysql> INSERT INTO tset VALUES (2); -- Statement does not take effect because "test" is misspelled as "tset".
ERROR 1146 (42S02): Table 'test.tset' doesn't exist
mysql> INSERT INTO test VALUES (1),(2); -- Entire statement does not take effect because it violates a PRIMARY KEY constraint
ERROR 1062 (23000): Duplicate entry '1' for key 'PRIMARY'
mysql> INSERT INTO test VALUES (3);
Query OK, 1 row affected (0.00 sec)
mysql> COMMIT;
Query OK, 0 rows affected (0.01 sec)
mysql> SELECT * FROM test;
+----+
| id |
+----+
| 1 |
| 3 |
+----+
2 rows in set (0.00 sec)
上記の例では、 INSERTステートメントが失敗した後もトランザクションは開いたままになります。その後、最後の挿入ステートメントが成功し、変更がコミットされます。
トランザクションサイズの制限
基盤となるストレージエンジンの制限により、TiDBでは1行が6MB以下である必要があります。行のすべての列は、データ型に従ってバイトに変換され、合計されて1つの行のサイズが推定されます。
TiDBは、楽観的トランザクションと悲観的トランザクションの両方をサポートしており、楽観的トランザクションは悲観的トランザクションの基礎です。楽観的なトランザクションは最初にプライベートメモリに変更をキャッシュするため、TiDBは単一のトランザクションのサイズを制限します。
デフォルトでは、TiDBは単一トランザクションの合計サイズを100MB以下に設定します。このデフォルト値は、構成ファイルのtxn-total-size-limitを介して変更できます。 txn-total-size-limitの最大値は1TBです。個々のトランザクションサイズの制限は、サーバーで使用可能な残りのメモリのサイズにも依存します。これは、トランザクションが実行されると、TiDBプロセスのメモリ使用量がトランザクションサイズと比較してスケールアップされ、トランザクションサイズの2〜3倍以上になるためです。
TiDBは以前、1つのトランザクションのキーと値のペアの総数を300,000に制限していました。この制限はTiDBv4.0で削除されました。
ノート:
通常、TiDB Binlogは、データをダウンストリームに複製するために有効になっています。一部のシナリオでは、Kafkaなどのメッセージミドルウェアを使用して、ダウンストリームに複製されるbinlogを消費します。
Kafkaを例にとると、Kafkaの単一メッセージ処理機能の上限は1GBです。したがって、
txn-total-size-limitが1 GBを超えるように設定されている場合、トランザクションはTiDBで正常に実行される可能性がありますが、ダウンストリームのKafkaはエラーを報告します。この状況を回避するには、最終消費者の制限に応じて実際の値txn-total-size-limitを決定する必要があります。たとえば、Kafkaをダウンストリームで使用する場合、txn-total-size-limitは1GBを超えてはなりません。
因果整合性
ノート:
因果整合性のあるトランザクションは、非同期コミットおよび1フェーズコミット機能が有効になっている場合にのみ有効になります。 2つの機能の詳細については、
tidb_enable_async_commitとtidb_enable_1pcを参照してください。
TiDBは、トランザクションの因果整合性の有効化をサポートしています。因果整合性のあるトランザクションは、コミットされると、PDからタイムスタンプを取得する必要がなく、コミットの待ち時間が短くなります。因果整合性を有効にする構文は次のとおりです。
START TRANSACTION WITH CAUSAL CONSISTENCY ONLY;
デフォルトでは、TiDBは線形の一貫性を保証します。線形整合性の場合、トランザクション1がコミットされた後にトランザクション2がコミットされると、論理的には、トランザクション2はトランザクション1の後に発生するはずです。因果整合性は線形整合性よりも弱いです。因果整合性の場合、2つのトランザクションのコミット順序と発生順序は、トランザクション1とトランザクション2によってロックまたは書き込まれたデータに交差がある場合にのみ一貫性が保証されます。つまり、2つのトランザクションには因果関係があります。データベース。現在、TiDBは外部の因果関係の受け渡しをサポートしていません。
因果整合性が有効になっている2つのトランザクションには、次の特性があります。
- 潜在的な因果関係を持つトランザクションには、一貫した論理順序と物理コミット順序があります
- 因果関係のないトランザクションは、一貫した論理順序と物理コミット順序を保証しません
- ロックなしの読み取りは因果関係を作成しません
潜在的な因果関係を持つトランザクションには、一貫した論理順序と物理コミット順序があります
トランザクション1とトランザクション2の両方が因果整合性を採用し、次のステートメントが実行されていると想定します。
| トランザクション1 | トランザクション2 |
|---|---|
| 因果整合性のみでトランザクションを開始する | 因果整合性のみでトランザクションを開始する |
| x = SELECT v FROM t WHERE id = 1 FOR UPDATE | |
| UPDATE t set v = $(x + 1)WHERE id = 2 | |
| 専念 | |
| UPDATE t SET v = 2 WHERE id = 1 | |
| 専念 |
上記の例では、トランザクション1がid = 1のレコードをロックし、トランザクション2がid = 1のレコードを変更します。したがって、トランザクション1とトランザクション2には潜在的な因果関係があります。因果整合性を有効にしても、トランザクション1が正常にコミットされた後にトランザクション2がコミットされる限り、論理的には、トランザクション2はトランザクション1の後に発生する必要があります。したがって、トランザクションがトランザクションを読み取らずにid = 1レコードのトランザクション2の変更を読み取ることはできません。 id = 2レコードの1の変更。
因果関係のないトランザクションは、一貫した論理順序と物理コミット順序を保証しません
id = 1とid = 2の初期値は両方とも0であると仮定します。トランザクション1とトランザクション2の両方が因果整合性を採用し、次のステートメントが実行されていると想定します。
| トランザクション1 | トランザクション2 | トランザクション3 |
|---|---|---|
| 因果整合性のみでトランザクションを開始する | 因果整合性のみでトランザクションを開始する | |
| UPDATE t set v = 3 WHERE id = 2 | ||
| UPDATE t SET v = 2 WHERE id = 1 | ||
| 始める | ||
| 専念 | ||
| 専念 | ||
| SELECT v FROM t WHERE id IN(1、2) |
上記の例では、トランザクション1はid = 1レコードを読み取らないため、トランザクション1とトランザクション2にはデータベースに知られている因果関係はありません。トランザクションの因果整合性が有効になっている場合、物理的な時間順序でトランザクション1がコミットされた後にトランザクション2がコミットされたとしても、TiDBはトランザクション2がトランザクション1の後に論理的に発生することを保証しません。
トランザクション1がコミットされる前にトランザクション3が開始され、トランザクション2がコミットされた後にトランザクション3がid = 1およびid = 2レコードを読み取る場合、トランザクション3はid = 1の値を2に読み取り、 id = 2の値を0に読み取る可能性があります。
ロックなしの読み取りは因果関係を作成しません
トランザクション1とトランザクション2の両方が因果整合性を採用し、次のステートメントが実行されていると想定します。
| トランザクション1 | トランザクション2 |
|---|---|
| 因果整合性のみでトランザクションを開始する | 因果整合性のみでトランザクションを開始する |
| UPDATE t SET v = 2 WHERE id = 1 | |
| SELECT v FROM t WHERE id = 1 | |
| UPDATE t set v = 3 WHERE id = 2 | |
| 専念 | |
| 専念 |
上記の例では、ロックなしの読み取りは因果関係を作成しません。トランザクション1とトランザクション2は書き込みスキューを作成しました。この場合、2つのトランザクションがまだ因果関係を持っていたとしたら、それは不合理だったでしょう。したがって、因果整合性が有効になっている2つのトランザクションには、明確な論理順序がありません。