0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

年越し準備は万全でしょうか。
我が家大晦日の夕食は、息子のねだりでスシローになりそうです。

お待たせしました、分散トランザクションの検証を続けます。

検証シナリオ

A、B銀行間での口座振替を分散トランザクションに見立てます

1. A銀行の口座からお金を引き出す
2. B銀行の口座にお金を振り込む
スクリーンショット 2021-12-30 20.42.21.png
口座振替を上記2つのオペレーションによる1セットとみなすと、
A銀行とB銀行二つのシステム(データベース)に処理が分散され、
どちらか一方だけでは振替が成立せず、分散トランザクションとみることができます。

検証準備

端末を三つ用意します

端末1: TiDBクラスタを起動
端末2: トランザクションを実行
端末3: 結果やステータスを確認

端末1から

TiDBクラスタを起動

$ tiup playground --db 2 --pd 3 --kv 3

端末2から

TiDBに接続

$ tiup client

A銀行データベースa_bank、口座テーブルaccountを作成

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を作成

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

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)

トランザクションのステータスを確認すると、Idle状態で存在しているようです

select * from information_schema.tidb_trx\G
*************************** 1. row ***************************
ID: 430153886054744065
START_TIME: 2021-12-31T08:27:32.931+09:00
CURRENT_SQL_DIGEST: 
CURRENT_SQL_DIGEST_TEXT: 
STATE: Idle
WAITING_START_TIME: 
MEM_BUFFER_KEYS: 2
MEM_BUFFER_BYTES: 82
SESSION_ID: 7
USER: root
DB: b_bank
ALL_SQL_DIGESTS: ["f53534242526f5f17dd442bf4429823244510563047efdcc0679741ca5833ea6","df9f53eb4cb1672927cb6374dd27fa80a8124ec0186f390ea41cac113d8428ac","dc5f0aff8b925c386a79b6e33f64e79e814e2310a12f0d793fe9ab4c9c8c0eee","28eafac12d18f34fb1bd6a857b73b37fb446d04eacbc3e18834f07ac9eb3c352","e82a3bae628989f0af1c7ce80dd40ddb0eb532f0e4f245e3f07280da22509428","c4ef5a83e75b42191d7e4e51ff33f5a712b8b41e743952a668b405076ce134ac","d7815f6adab7ceebe67882038b7d29c081675c0e94751110ff74213523b6998e"]

端末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)

なんだ、コミットされていたのではなく、下書き(Prewrite)されていただけか。

端末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

気を取り直し、再度正常系の振替を実施

トランザクション開始

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とMySQLの差異に注意

トランザクション処理において、TiDBとMySQLで差異が存在するようです。

スナップショットの取得タイミング

TiDBでは、BEGINSTART TRANSACTION実行直後に、データベースのスナップショットを取得するのに対し、
MySQLでは、BEGINSTART TRANSACTIONでトランザクション開始後、InnoDBからデータ取得する最初のSELECT(SELECT FOR UPDATEは対象外)が実行されたらスナップショットを取得する。

要するに、TiDBの
BEGIN,
START TRANSACTION,
START TRANSACTION WITH CONSISTENT SNAPSHOT
などのコマンドは、MySQLの
START TRANSACTION WITH CONSISTENT SNAPSHOT
コマンドに相当するらしいです。

一意制約のチェックタイミング

TiDBの楽観的トランザクションで、プライマリキーや一意制約のチェックタイミングは、DML実行時でなく、COMMIT時に行われる、がデフォルト設定のようです。

詳細は、PingCAP社のドキュメントをご参照ください。
https://docs.pingcap.com/tidb/stable/transaction-overview

終わりに

SQL操作からは、TiDBの方がMySQLのXAトランザクションに比べ、シンプルのようです。
次回は、TiDB分散トランザクションの特性を、いくつかの側面から検証してみます。
お楽しみに。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?