MySQL vs. TiDB-分散トランザクションの比較検証(1): トランザクションの基本

## はじめに

MySQLとTiDBを使った、分散トランザクション比較検証をスタートします。

- MySQL
    - お馴染みのオープンソースのリレーショナルデータベース管理システム
- TiDB
    - ハイブリッド・トランザクション/分析処理(HTAP)をサポートするオープンソースで分散型のNewSQLデータベース
    - MySQLと互換性があり、水平方向のスケーラビリティ、強力な一貫性、高可用性を提供するようです([PingCAP社](https://pingcap.co.jp/))

## まずは理論武装から

関連知識をざっとおさらいします。

### トランザクションとは

- 分けることのできない一連の情報処理の一単位を意味する、この一連の処理を分割して実行した場合、結果の整合性を保てなくなる
※　Wikipediaから抜粋: [トランザクション](https://ja.wikipedia.org/wiki/%E3%83%88%E3%83%A9%E3%83%B3%E3%82%B6%E3%82%AF%E3%82%B7%E3%83%A7%E3%83%B3#%E3%82%B3%E3%83%B3%E3%83%94%E3%83%A5%E3%83%BC%E3%82%BF%E3%82%B7%E3%82%B9%E3%83%86%E3%83%A0%E3%81%AB%E3%81%8A%E3%81%91%E3%82%8B%E3%83%88%E3%83%A9%E3%83%B3%E3%82%B6%E3%82%AF%E3%82%B7%E3%83%A7%E3%83%B3%E5%87%A6%E7%90%86)

- トランザクションと言ったら、ACID特性が重要ですかね。
    - Atomicity(原子性)
    - Consistency(一貫性)
    - Isolation(分離性)
    - Durability(永続性)

トランザクションに含まれる個々の手順が「すべて実行される」か「一つも実行されない」のどちらかの状態になるのが肝のようです。

- よくある、銀行口座からの引き出しの例

1トランザクションに二つ処理が必要

処理1:　指定金額を引き出す
処理2:　預金残高を更新する

処理1だけ実行し処理2を省いたら、預金残高が変わらず永遠に引き出せてしまいますね。

### では、分散トランザクションとは

- トランザクション処理の処理形態の1つであり、ネットワーク上の2つ以上のホスト(処理するコンピュータ)が関連する、1まとまりの操作(処理、取引、トランザクション)のことを示す
※　Wikipediaから抜粋: [分散トランザクション](https://ja.wikipedia.org/wiki/%E5%88%86%E6%95%A3%E3%83%88%E3%83%A9%E3%83%B3%E3%82%B6%E3%82%AF%E3%82%B7%E3%83%A7%E3%83%B3)

- 要点二つありました
    - トランザクションが複数ノード(または、データベース、ホスト)間にまたがる
        - 処理が複数データベースに分散されるので処理の整合性を保つが難しそう
    - ACIDを保証する一般的な方法は2フェーズコミットプロトコルである
        - 2PC(Two-Phase Commit)とは、複数のノードで処理の整合性が保たれるよう2段階に分けてコミットを行う手法

- 旅行予約を例に

複数システムによる分散トランザクションと考えた場合、

処理1: 航空券の予約
処理2: レンタカーの予約
処理3: ホテルの予約

- 2フェーズコミットに含まれるフェーズ(もっと複雑ですが、イメージとして)
    - フェーズ１: 事前確認
        - 上記三つのシステムに対し予約(コミット)可否を問い合わせる
        - いずれから「ごめんなさい、予約できません」と返されたら、旅行をキャンセル(ロールバック)し、なかったことに
    - フェーズ２: 正式コミット
        - フェーズ１で全てのシステムからOK返された
        - 正式コミット(支払い)し、旅に出る

## MySQLの分散トランザクション

### [XAトランザクション](https://dev.mysql.com/doc/refman/5.6/ja/xa.html)機能により実現

- 分散トランザクション、つまり、複数の個別トランザクションリソースがグローバルトランザクションに参加することを許可する機能をサポート
- トランザクションリソースは多くの場合RDBMSだが、ほかの種類のリソースであってもかまわない
- グローバルトランザクションには、トランザクションである複数のアクションが含まれ、そのすべてがグループとして正常に完了するか、またはすべてがグループとしてロールバックされるかのどちらかである必要がある
- グローバルトランザクションを使用するアプリケーションには、1つまたは複数のリソースマネージャーと 1 つのトランザクションマネージャーが含まれる
    - リソースマネージャー(RM)は、トランザクションリソースへのアクセスを提供
        - データベースサーバーも、一種リソースマネージャー
        - RMによって管理されているトランザクションをコミットまたはロールバックできる必要あり
    - トランザクションマネージャー(TM)は、グローバルトランザクションの一部であるトランザクションを調整
        - 各トランザクションを処理するRMと通信

## TiDBは分散トランザクションの課題をどう解決するか

### トランザクションモデル
Googleの[Percolator](http://static.googleusercontent.com/media/research.google.com/zh-CN//pubs/archive/36726.pdf)とXiaoMiの[Themis](https://github.com/XiaoMi/themis) からインスピレーションを受け、さらに下記最適化を施したらしいです。

- Timestamp Oracle(TSO)の最適化
    - Percolatorライクのシステムは、単調に増加するタイムスタンプを割り当てるため、TSOと呼ばれるグローバルに一意のタイムサービスが必要
    - ※トランザクション制御にタイムサービスが大事と言うことですね
- ロック処理速度の最適化
    - Percolatorでは特定の行に列を追加してロックなどの情報を保存するのに対し、RocksDBの列ファミリー(CF)を使用して、ロックに関連するすべての情報を処理
    - 大量データの場合、同時トランザクションにおける行ロックは多くないため、最適化された追加CFに配置することでロック処理速度が大幅に向上するとか
    - ※ トランザクションにロック制御が欠かせませんのでこれも大事か
- 残留ロックのクリーンアップの最適化
    - トランザクションによって取得された行ロックが、スレッドのクラッシュまたはその他理由でクリーンアップされず、かつ残留ロックにアクセスする後続トランザクションが存在しない場合、ロックは取り残されてしまう
    - 列ファミリー(CF)を使用する利点は、CFをスキャンすることで、これらのロックを簡単に検出してクリーンアップできる

## おわりに

理論だけではピンと来ませんので、次回から検証しながら理解を深めます。
お楽しみに。

##### [次回] [MySQL vs. TiDB-分散トランザクションの比較検証(2): MySQL XA](https://qiita.com/mingchun_zhao/items/803bd99181974f63f4ce)