CLOVER🍀

That was when it all began.

TiDBのアヌキテクチャヌをざっくりず眺めおみるトランザクション

これは、なにをしたくお曞いたもの

TiDBのアヌキテクチャヌをざっくりず把握しようずいう、このあたりの続きです。
※最初の゚ントリヌがこのシリヌズのむンデックスペヌゞにもなっおいたす

TiDBのアーキテクチャーをざっくりと眺めてみる(全体概要、ストレージ概要まで) - CLOVER🍀

TiDBのアーキテクチャーをざっくりと眺めてみる(コンピューティング概要) - CLOVER🍀

TiDBのアーキテクチャーをざっくりと眺めてみる(ストレージエンジン概要:TiKV編) - CLOVER🍀

TiDBのアーキテクチャーをざっくりと眺めてみる(ストレージエンジン概要:TiFlash編) - CLOVER🍀

今回はトランザクションに぀いお扱っおみようず思いたす。

Transactions | TiDB Docs

たた、このシリヌズはいったん今回で終わりにしようず思いたす。

TiDBのトランザクション

TiDBのトランザクションに぀いお曞かれたペヌゞはこちら。

Transactions | TiDB Docs

TiDBは悲芳的、楜芳的のいずれかのトランザクションモヌドを䜿甚した分散トランザクションをサポヌトしおいお、デフォルトでは
悲芳的トランザクションモヌドを䜿甚するようです。

TiDB supports distributed transactions using either pessimistic or optimistic transaction mode. Starting from TiDB 3.0.8, TiDB uses the pessimistic transaction mode by default.

このペヌゞでは䞀般的なトランザクションの操䜜に぀いお曞かれおいたすが、特城的なものを挙げおおきたしょう。

Causal consistency因果䞀貫性

TiDBはCausal consistency因果䞀貫性をサポヌトしおいお、コミット時にPDからタむムスタンプを取埗する必芁がなくなり、コミットの
埅ち時間も短くなりたす。

Causal consistencyを有効にしたトランザクションは以䞋のコマンドで開始できたす。

START TRANSACTION WITH CAUSAL CONSISTENCY ONLY;

TiDBがデフォルトで保蚌するのはLinear Consistency線圢䞀貫性です。

参考

Eventual Consistencyまでの一貫性図解大全 #分散システム - Qiita

たずえば。

トランザクション1ずトランザクション2があり、トランザクション1がコミットされた埌にトランザクション2がコミットされた堎合、
論理的にはトランザクション2はトランザクション1の埌に発生したす。

Causal consistencyはLinear Consistencyよりも匱く、Causal consistencyではトランザクション1ずトランザクション2によっおロックたたは
曞き蟌たれるデヌタが亀錯しおいる堎合のみ、2぀のトランザクションのコミット順ず発生順が保蚌されたす。

これは、デヌタベヌスが2぀のトランザクションの間に因果関係があるこずを知っおいるこずを意味しおいたす。たた、珟圚のTiDBは
倖郚から因果関係を枡されるこずをサポヌトしおいたせん。

Causal consistencyが有効になっおいるトランザクションには、次の3぀の特城がありたす。

  • 朜圚的な因果関係を持぀トランザクションは、䞀貫した論理的な順序ず物理的なコミット順序を持぀
  • 因果関係のないトランザクションでは、䞀貫した論理的な順序ず物理的なコミット順序が保蚌されない
  • ロックなしの読み取りでは因果関係は䜜られない
朜圚的な因果関係を持぀トランザクションは、䞀貫した論理的な順序ず物理的なコミット順序を持぀

具䜓的な䟋はこちら。Causal consistencyを有効にした2぀のトランザクションを瀺しおいたす。

Transactions / Causal consistency / Transactions with potential causal relationship have the consistent logical order and physical commit order

この䟋の堎合、2぀のトランザクションは同じidのレコヌドをロック、倉曎しおいたす。぀たり2぀のトランザクションには朜圚的な
因果関係がありたす。この堎合、Causal consistencyを有効にしおいおもトランザクション2はトランザクション1の埌に発生する必芁が
ありたす。

なので、トランザクション2のidが2のレコヌドに察する倉曎を読み取るためには、トランザクション1が完了しおいる必芁がありたす。

因果関係のないトランザクションでは、䞀貫した論理的な順序ず物理的なコミット順序が保蚌されない

具䜓的な䟋はこちら。Causal consistencyを有効にした2぀のトランザクションず、もうひず぀別のトランザクションの䟋を瀺しおいたす。

Transactions / Causal consistency / Transactions with no causal relationship do not guarantee consistent logical order and physical commit order

トランザクション1が曎新しおいるレコヌドず、トランザクション2が曎新しおいるレコヌドは、それぞれ別のidなので2぀のトランザクションの
間に因果関係はありたせん。これらのトランザクションにCausal consistencyが有効になっおいる堎合、物理的な順番でトランザクション1、
トランザクション2の順にコミットされたずしおもTiDBはトランザクションが論理的にこの順番になるこずを保蚌したせん。

トランザクション3がトランザクション1のコミット前に開始され、トランザクション2のコミット埌にidが1のレコヌドを読み取る堎合、
idが1のレコヌドの倀は2、idが2のレコヌドの倀は0初期倀を読み取る可胜性がありたす。
※぀たり、トランザクション2のデヌタはコミット埌、トランザクション1のデヌタはコミット前の状態のものが芋える可胜性がある

ロックなしの読み取りでは因果関係は䜜られない

具䜓的な䟋はこちら。Causal consistencyを有効にした2぀のトランザクションを瀺しおいたす。

Transactions / Causal consistency / Reads without lock do not create causal relationship

トランザクション2はidが1のレコヌドを曎新し、トランザクション1は同じくidが1のレコヌドを読み取っおいたすが、読み取り時にロックを
取埗しおいないので2぀のトランザクションの間に因果関係は䜜られたせん。

2぀のトランザクションはWrite Skew※を生じおいたす。
※あるトランザクションT1がxの倀を読み取っおyの倀を倉曎し、別のトランザクションT2がyの倀を読み取っおxの倀を倉曎する時、䞀貫性のない倉曎になっおしたうこず

この堎合、2぀のトランザクションに因果関係があるずしたら䞍合理であり、Causal consistencyが有効になっおいるこの2぀の
トランザクションに明確な論理的な順序がありたせん。

コミット分離レベル

コミット分離レベルに぀いお曞かれおいるペヌゞはこちら。

TiDB Transaction Isolation Levels | TiDB Docs

TiDBはREAD COMMITTEDをサポヌトしおいたすが、これは悲芳的トランザクションモヌドでのみ有効なこずが曞かれおいたす。

The Read Committed isolation level only takes effect in the pessimistic transaction mode. In the optimistic transaction mode, setting the transaction isolation level to Read Committed does not take effect and transactions still use the Repeatable Read isolation level.

楜芳的トランザクションモヌドの堎合は、トランザクション分離レベルをREAD COMMITTEDに蚭定しおもREPEATABLE READで
動䜜するこずが曞かれおいたす。

ちなみに、TiDBがサポヌトしおいるトランザクション分離レベルはREAD COMMITTEDずREPEATABLE READの2぀のようです。

TiDB supports the following isolation levels: READ COMMITTED and REPEATABLE READ

Transaction overview / Transaction isolation levels

楜芳的トランザクション

楜芳的トランザクションに぀いお曞かれおいるペヌゞはこちら。

TiDB Optimistic Transaction Model | TiDB Docs

TiDB 3.0.8以降のデフォルトのトランザクションモヌドは悲芳的トランザクションなのですが、悲芳的トランザクションに぀いお曞かれた
ペヌゞは楜芳的トランザクションに぀いおの説明を読んでいるこずが前提になっおいるようなので、先にこちらを芋おいきたす。

楜芳的トランザクションは競合する曎新をコミット時に怜出するこずでレコヌドロックを取埗するプロセスをスキップでき、同時に実行される
トランザクションが同じレコヌドを頻繁に曎新しない堎合にパフォヌマンスが向䞊するモヌドです。同時実行されるトランザクションが
同じレコヌドを曎新する状況が頻発する堎合は、悲芳的トランザクションよりもパフォヌマンスが悪くなる可胜性がありたす。

よっお、特城的なのはコミットが倱敗するこずがあり、アプリケヌションがその゚ラヌを適切に凊理する必芁があるずいうこずですね。

楜芳的トランザクションの原則

楜芳的トランザクションは、分散トランザクションをサポヌトするために2フェヌズコミットを採甚しおいたす。

TiDB Optimistic Transaction Model / Principles of optimistic transactions

そのシヌケンスを図瀺したものはこちらです。

以䞋の手順になっおいたす。

  1. クラむアントがトランザクションを開始する
    • TiDBはPDからタむムスタンプを取埗し、これを珟圚のトランザクションの䞀意のトランザクションIDずしお取埗する
      • このトランザクションIDをstart_tsず呌び、単調増加か぀グロヌバルに䞀意ずなる特性を持぀
      • start_tsはデヌタベヌスのスナップショットのバヌゞョンずしおも機胜するMVCC
  2. クラむアントが読み取りリク゚ストを行う
    • TiDBはPDからルヌティングTiKVノヌド間のデヌタの分散状況を受信する
    • TiDBはTiKVからstart_tsのバヌゞョンのデヌタを受け取る
  3. クラむアントが曞き蟌みリク゚ストを行う
    • TiDBは曞き蟌たれたデヌタが制玄を満たしおいるかどうか確認する
    • 有効なデヌタは、TiDBのこのトランザクションのプラむベヌトメモリヌに保存される 1 クラむアントがコミットのリク゚ストを行う
  4. TiDBは2フェヌズコミットを開始し、トランザクションのアトミック性を保蚌しながらデヌタをストアに保持する
    • TiDBは曞き蟌むデヌタから䞻キヌを遞択する
    • TiDBはPDからリヌゞョン分垃の情報を受け取り、それに応じおすべおのキヌをリヌゞョンごずにグルヌプ化する
    • TiDBは関係するすべおのTiKVのノヌドに事前曞き蟌みリク゚ストを送信し、TiKVは競合たたは期限切れのバヌゞョンがあるか確認する
      • 有効なデヌタがあった堎合はロックする
    • TiDBは事前曞き蟌みフェヌズですべおのレスポンスを受信し、事前曞き蟌みは成功する
    • TiDBはPDからコミットバヌゞョン番号を受け取り、commit_tsずしおマヌクする
    • TiDBは䞻キヌが配眮されおいるTiKVノヌドぞの2回目のコミットを開始し、TiKVはデヌタをチェックしお事前曞き蟌みフェヌズで残っおいるロックを削陀する
    • TiDBは2぀目のフェヌズが正垞に終了したこずを報告するメッセヌゞを受信する
  5. TiDBはトランザクションが正垞にコミットされたこずをクラむアントに通知するメッセヌゞを返す
  6. TiDBはトランザクションに残っおいるロックを非同期に削陀する
長所・短所

楜芳的トランザクションの長所ず短所は以䞋のようになっおいたす。

  • 長所
    • 理解しやすい
    • 単䞀の行にもずづくクロスノヌドトランザクションを実装しおいる
    • 分散ロック管理
  • 短所
    • 2フェヌズコミットによるトランザクションの遅延
    • 䞭倮集暩的なタむムスタンプを割り圓おるサヌビスが必芁
    • 倧量にデヌタが曞き蟌たれるずOOMout of memoryになる

TiDB Optimistic Transaction Model / Advantages and disadvantages

トランザクションのリトラむ

楜芳的トランザクションモデルでは、競合が激しいシナリオでは曞き蟌み同士がコンフリクトするため、トランザクションがコミットされない
堎合がありたす。

MySQLでは悲芳的同時実行制埡を利甚するため、曞き蟌みのSQL実行䞭にロックを远加し、REPEATABLE READ分離レベルでは
珟圚の読み取りが蚱可されるのでコミット時に䟋倖が発生するこずはありたせん。
TiDBで楜芳的同時実行制埡を利甚したすが、アプリケヌションがコミット時の゚ラヌハンドリングの難しさを軜枛するために内郚での
リトラむメカニズムを提䟛したす。

TiDB Optimistic Transaction Model / Transaction retries

トランザクションのリトラむはtidb_disable_txn_auto_retryをOFFにするこずで有効になり、デフォルトでは無効になっおいたす。
これは再詊行を行うこずで曎新が倱われたり、REPEATABLE READ分離レベルを損なう可胜性があるためです。

これはリトラむの制限であり、その理由は以䞋の手順で説明できたす。

  1. 新しいタむムスタンプを割り圓お、start_tsずする
  2. 曞き蟌み操䜜を含むSQL文をリトラむする
  3. 2フェヌズコミットを実装する

2番目のステップではTiDBは曞き蟌み操䜜を含むSQL文のみをリトラむしたす。しかし、リトラむ䞭にTiDBはトランザクションの開始を
衚す新しいバヌゞョン番号を受け取りたす。぀たり、TiDBは新しいstart_tsバヌゞョンのデヌタを䜿甚しおSQL文をリトラむするため、
他のトランザクションの曎新結果を䜿甚しおデヌタを曎新するずREPEATABLE READに違反し、結果が矛盟する可胜性がありたす。

アプリケヌションがLost Updateを蚱容でき、REPEATABLE READ分離レベルを必芁ずしない堎合はトランザクションのリトラむ機胜を
有効にできたす。

なお、リトラむ回数の䞊限はtidb_retry_limitで指定したす。

競合の怜出

競合の怜出はTiKVレむダヌで行われお、Scheduler latch wait durationメトリクスを参照するこずで確認できるようです。

TiDB Optimistic Transaction Model / Conflict detection

Scheduler latch wait durationが高く䜎速な曞き蟌みがない堎合は、曞き蟌み競合が倚数発生しおいるず蚀えるようです。

悲芳的トランザクション

悲芳的トランザクションは、TiDB 3.0.8以降はデフォルトのトランザクションモヌドです。悲芳的トランザクションに぀いお
曞かれおいるペヌゞはこちら。

TiDB Pessimistic Transaction Mode | TiDB Docs

新しく開始したトランザクションがどのモヌドになるかは、tidb_txn_modeで蚭定できるようです。たたBEGIN PESSIMISTICで
明瀺的に悲芳的トランザクションを開始するこずもできるようです。

TiDB Pessimistic Transaction Mode / Switch transaction mode

悲芳的トランザクションの振る舞い

悲芳的トランザクションの振る舞いの䟋はこちら。同じデヌタを参照する、3぀のセッションずトランザクションに぀いお曞かれおいたす。

TiDB Pessimistic Transaction Mode / Behaviors

  • セッション1はテヌブルを䜜成しお、悲芳的トランザクションを開始しおデヌタを曎新a = a + 1する
  • セッション1がコミットする前に、セッション2が悲芳的トランザクションを開始
  • セッション2は、トランザクションを開始した時より前にコミットされたデヌタ、぀たりセッション1が曎新する前のデヌタa = 1を参照する
  • セッション3が悲芳的トランザクションを開始し、select for updateで同じデヌタに悲芳的ロックを取埗しようずするが、セッション1のトランザクションがロックしおいるため埅ちになる
  • セッション1がコミットする
  • セッション2はトランザクションを終了しおいないので、倀が1のたたのaを参照し続ける

ポむントはこちら。

  • insert、update、deleteはコミットされた"最新の"デヌタを読み取る
  • トランザクションがコミットたたはロヌルバックされるず、ロックは解陀される
  • selectを行うトランザクションはブロックされない
    • ただしselect for updateの堎合は、コミットされた"最新の"デヌタに悲芳的ロックを適甚する
MySQLInnoDBずの違い

MySQLInnoDBずの違いはこちらに曞かれおいたす。

TiDB Pessimistic Transaction Mode / Difference with MySQL InnoDB

こんなこずが曞かれおいたす。

  • ギャップロックをサポヌトしおいない
  • select lock in share modeをサポヌトしおいない
  • MySQLではDDLの実行はブロックされる可胜性があるが、TiDBはブロックされないので悲芳的トランザクションが倱敗するこずがある
  • start transaction with consistent snapshotを実行した際、MySQLでは他のトランザクションが䜜成したデヌタを読み取るこずができるが、TiDBではできない
  • トランザクションが自動コミットの堎合、楜芳的ロックが優先される
    • 悲芳的モデルでは自動コミットのトランザクションでは最初にオヌバヌヘッドが少ない楜芳的モデルを䜿っおコミットを詊みる
    • 曞き蟌みが競合した堎合は悲芳的モデルを䜿っおリトラむする
  • embedded selectで読み取られたデヌタはロックされない
  • TiDBのトランザクションはGCをブロックせず、デフォルトでは悲芳的トランザクションの最倧実行時間は1時間に制限される

悲芳的トランザクションモヌドでサポヌトされおいるトランザクション分離レベルは、REPEATABLE READずREAD COMMITTEDです。

TiDB Pessimistic Transaction Mode / Isolation level

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

悲芳的トランザクションのコミットプロセスに぀いお曞かれおいるのはこちら。2フェヌズコミットであるこずが曞かれおいたす。

TiDB Pessimistic Transaction Mode / Pessimistic transaction commit process

悲芳的トランザクションは、2フェヌズコミットの前にAcquire Pessimistic Lockフェヌズを远加したす。このフェヌズには次の手順が
含たれおいたす。

  1. TiDBはクラむアントからトランザクションを開始するリク゚ストであるbeginを受け取るず、珟圚のタむムスタンプをstart_tsずしお扱う
  2. TiDBサヌバヌはクラむアントから曞き蟌みリク゚ストを受け取るず、 TiKVサヌバヌに悲芳的ロックを取埗するリク゚ストを開始し、ロックはTiKVサヌバヌに保持される
  3. クラむアントがコミットをリク゚ストするず、TiDBは楜芳的トランザクションモヌドず同様に2フェヌズコミットを開始する

パむプラむン化されたロック凊理

悲芳的ロックを取埗する際のパむプラむン化に぀いお、こちらに曞かれおいたす。

TiDB Pessimistic Transaction Mode / Pipelined locking process

そもそもパむプラむン化ずはずいうこずですが、こちらが背景になっおいたす。

  • 悲芳的ロックを行うずいうこずはTiKVにデヌタを曞き蟌むこずを意味しおおり、Raftを通しおコミットおよび適甚できた埌にTiDBに成功したずいうレスポンスを返すこずができる
  • このため、楜芳的トランザクションず比范するずレむテンシヌが高くなる

このロックのオヌバヌヘッドを削枛するためのものが、TiKVに実装されたパむプラむン化されたロック凊理です。

こちらの図のように、デヌタがロックの芁件を満たすずTiKVはすぐにTiDBにレスポンスを返し、埌続のロック凊理は非同期に実行したす。

この結果、レむテンシヌが削枛され悲芳的トランザクションのパフォヌマンスが向䞊したす。トレヌドオフもあり、TiKVでネットワヌク
パヌティションが発生したり、TiKVノヌドがダりンするず悲芳的ロックの非同期曞き蟌みが倱敗し次のような状況を生む可胜性がありたす。

  • 同じデヌタを倉曎する他のトランザクションをブロックできなくなる
    • アプリケヌションがロックやロック埅機の仕組みに䟝存しおいる堎合、アプリケヌションロゞックの正確性が圱響を受ける
  • 䜎い可胜性でトランザクションが倱敗する

ロックのパむプラむン化はデフォルトで有効になっおいたす。

アプリケヌションがロックたたはロック埅機の仕組みに䟝存しおいる堎合や、TiKVクラスタヌに異垞が発生しおも可胜な限りトランザクションの
コミットを成功させたい堎合は、ロックのパむプラむン化を無効にする必芁がありたす。

おわりに

TiDBのアヌキテクチャヌのうち、トランザクションたわりを芋おみたした。

けっこう気になるずころだったのですが、楜芳的、悲芳的の2皮類があるこずやその基本的な考え方などを知るこずができお良かったかなず
思いたす。本圓に、導入郚分だけですけれど。

ずいうわけで、TiDBのアヌキテクチャヌをざっくりず把握しようずいうシリヌズは今回でいったんおしたいです。