[前回] MySQL vs. TiDB-分散トランザクションの比較検証(8): TiDB分散トランザクションの基本動作
はじめに
今回は、TiDB分散トランザクションの例として、銀行振込みのシミュレーションです。
検証シナリオ
分散トランザクションの例として、A銀行
からB銀行
へ振込みを行います。
分散トランザクションは、以下2操作で構成されます。
① A銀行
口座から10万円引出す
② B銀行
口座に10万円振込む
処理が二つのシステム(データベース)に分散され、
どちらか失敗しても、振込みは成立しません。
検証準備
端末を三つ用意します
端末1: TiDBクラスタを起動
端末2: TiDBトランザクションを実行
端末3: 別セッションからTiDBステータスを確認
端末1から
TiDBクラスタを起動
$ tiup playground --db 2 --pd 3 --kv 3
... ...
CLUSTER START SUCCESSFULLY, Enjoy it ^-^
To connect TiDB: mysql --comments --host 127.0.0.1 --port 4000 -u root -p (no password)
To connect TiDB: mysql --comments --host 127.0.0.1 --port 4001 -u root -p (no password)
To view the dashboard: http://127.0.0.1:2379/dashboard
PD client endpoints: [127.0.0.1:2379 127.0.0.1:2382 127.0.0.1:2384]
To view the Prometheus: http://127.0.0.1:9090
To view the Grafana: http://127.0.0.1:3000
端末2から
TiDBに接続
$ tiup client
Enterを押すと、コマンドプロンプトが表示されます。
my:root@127.0.0.1:4000=>
A銀行のデータベースとテーブルを作成
銀行データベース: a_bank
口座テーブル: account
レコードをINSERTします。
ユーザー: foo
残高: 50万円
CREATE DATABASE a_bank;
USE a_bank;
CREATE TABLE account(name VARCHAR(10) PRIMARY KEY, balance DECIMAL(10,2));
INSERT INTO account VALUES('foo', 500000);
B銀行のデータベースとテーブルを作成
銀行データベース: b_bank
口座テーブル: account
レコードをINSERTします。
ユーザー: foo
残高: 0
CREATE DATABASE b_bank;
USE b_bank;
CREATE TABLE account(name VARCHAR(10) PRIMARY KEY, balance DECIMAL(10,2));
INSERT INTO account VALUES('foo', 0);
端末3から
TiDBに接続
$ tiup client
Enterを押すと、コマンドプロンプトが表示されます。
my:root@127.0.0.1:4000=>
A、B銀行の口座残高を確認
SELECT * FROM a_bank.account;
name | balance
------+-----------
foo | 500000.00
(1 row)
SELECT * FROM b_bank.account;
name | balance
------+-----------
foo | 0.00
(1 row)
検証スタート
端末2から
分散トランザクション開始
START TRANSACTION;
START TRANSACTION
A銀行から10万円引き出す
UPDATE a_bank.account SET balance=balance-100000 WHERE name='foo';
UPDATE 1
B銀行に10万円振り込む
UPDATE b_bank.account SET balance=balance+100000 WHERE name='foo';
UPDATE 1
トランザクションの同じセッションから、A、B銀行残高を確認
SELECT * FROM a_bank.account;
name | balance
------+-----------
foo | 400000.00
(1 row)
SELECT * FROM b_bank.account;
name | balance
------+-----------
foo | 100000.00
(1 row)
コミットしていないのに、残高変わったようです(?)。
トランザクションモードを確認
SELECT @@tidb_txn_mode;
@@tidb_txn_mode
-----------------
pessimistic
(1 row)
悲観的モードになっています。
トランザクションのステータスを確認
SELECT * FROM information_schema.tidb_trx\G
*************************** 1. row ***************************
ID: 433178055110295555
START_TIME: 2022-05-13T20:59:02.358+09:00
CURRENT_SQL_DIGEST:
CURRENT_SQL_DIGEST_TEXT:
STATE: Idle
WAITING_START_TIME:
MEM_BUFFER_KEYS: 4
MEM_BUFFER_BYTES: 182
SESSION_ID: 407
USER: root
DB: b_bank
ALL_SQL_DIGESTS: ["f53534242526f5f17dd442bf4429823244510563047efdcc0679741ca5833ea6","df9f53eb4cb1672927cb6374dd27fa80a8124ec0186f390ea41cac113d8428ac","dc5f0aff8b925c386a79b6e33f64e79e814e2310a12f0d793fe9ab4c9c8c0eee","28eafac12d18f34fb1bd6a857b73b37fb446d04eacbc3e18834f07ac9eb3c352","e82a3bae628989f0af1c7ce80dd40ddb0eb532f0e4f245e3f07280da22509428","c4ef5a83e75b42191d7e4e51ff33f5a712b8b41e743952a668b405076ce134ac","d7815f6adab7ceebe67882038b7d29c081675c0e94751110ff74213523b6998e"]
Idle状態になっています。
端末3から
トランザクションとは別セッションから、A、B銀行残高を確認
SELECT * FROM a_bank.account;
name | balance
------+-----------
foo | 500000.00
(1 row)
SELECT * FROM b_bank.account;
name | balance
------+-----------
foo | 0.00
(1 row)
残高が更新されていません。
分散トランザクションを開始したセッションのみ、残高が更新された理由は、
現時点で、分散トランザクションのステータスが、
第1フェーズの下書き(Prewrite)
が終わっただけで、
第2フェーズのコミット
はまだ行われていないからです。
端末2から
分散トランザクションをロールバック
ROLLBACK;
ROLLBACK
A、B銀行残高を確認
SELECT * FROM a_bank.account;
name | balance
------+-----------
foo | 500000.00
(1 row)
SELECT * FROM b_bank.account;
name | balance
------+---------
foo | 0.00
(1 row)
残高が元とおりに戻りました。
トランザクションのステータスを確認
SELECT * FROM information_schema.tidb_trx\G
終了したので存在しません。
上記シナリオを再度実施し、コミット操作を確認
端末2から
分散トランザクション開始
START TRANSACTION;
START TRANSACTION
A銀行から10万円引き出す
UPDATE a_bank.account SET balance=balance-100000 WHERE name='foo';
UPDATE 1
B銀行に10万円振込む
UPDATE b_bank.account SET balance=balance+100000 WHERE name='foo';
UPDATE 1
コミット
COMMIT;
COMMIT
A、B銀行残高を確認
SELECT * FROM a_bank.account;
name | balance
------+-----------
foo | 400000.00
(1 row)
SELECT * FROM b_bank.account;
name | balance
------+-----------
foo | 100000.00
(1 row)
期待とおり口座振込みできました。
分散トランザクション成功、やったー。
トランザクションのステータスを確認
SELECT * FROM information_schema.tidb_trx\G
終了したので存在しません。
端末3から
トランザクションとは別セッションからも、A、B銀行残高を確認
SELECT * FROM a_bank.account;
name | balance
------+-----------
foo | 400000.00
(1 row)
SELECT * FROM b_bank.account;
name | balance
------+-----------
foo | 100000.00
(1 row)
別セッションからも、期待とおり残高更新が確認できました。
分散トランザクションにおける TiDB vs. MySQL
スナップショットの取得タイミングが異なる
- TiDB
-
BEGIN
やSTART TRANSACTION
でトランザクション開始直後に - データベースのスナップショットを取得
-
- MySQL
-
BEGIN
やSTART TRANSACTION
でトランザクション開始後 - InnoDBからデータ取得する最初の
SELECT
(SELECT FOR UPDATE
は対象外)が実行されたら - スナップショットを取得
-
言い換えると、TiDBの下記三つのコマンドは、
BEGIN
START TRANSACTION
START TRANSACTION WITH CONSISTENT SNAPSHOT
MySQLの下記コマンドに相当
START TRANSACTION WITH CONSISTENT SNAPSHOT
TiDBの一意制約レイジーチェックの最適化
-
Lazy check of constraints
とは- TiDBの楽観的トランザクションで
- プライマリキーや一意制約のチェックを
- COMMIT時に行う(通常のDML実行時でなく)
- メリット
- 制約チェックをバッチ処理し
- ネットワーク通信を減らすことで
- パフォーマンスを向上できる
- デフォルト設定の変更
- パラメータ
tidb_constraint_check_in_place
をTRUE
に設定すると - 本機能が無効に
- パラメータ
終わりに
TiDB分散トランザクション例を用いて、MySQL XAと比較しました。
次回は、分散トランザクションの同時実行制御を検証してみます。
お楽しみに。