はじめに
前回の記事TiDBのMVCCを理解する(1)では、TiDBのMVCC(Multi-Version Concurrency Control)について、TiDB Playgroundを使って実際に動作を確認しながら解説しました。今回は、前回解説しきれなかった部分について説明していきます。
準備
まず、前回同様、TiDB Playgroundを開き、レコードを挿入して更新しておきましょう。
> tiup playground
Note: Version constraint is resolved to v8.5.4. If you'd like to use other versions:
<省略>
🎉 TiDB Playground Cluster is started, enjoy!
Connect TiDB: mysql --host 127.0.0.1 --port 4000 -u root
TiDB Dashboard: http://127.0.0.1:2379/dashboard
Grafana: http://127.0.0.1:3000
接続できたら、テーブルを作成して、更新します。
mysql> USE test;
Database changed
mysql> CREATE TABLE mvcc(id INT AUTO_INCREMENT PRIMARY KEY, create_at DATETIME DEFAULT CURRENT_TIMESTAMP);
Query OK, 0 rows affected (0.03 sec)
レコードを3件挿入します。
mysql> INSERT INTO mvcc VALUES (),(),();
Query OK, 3 rows affected (0.01 sec)
Records: 3 Duplicates: 0 Warnings: 0
mysql> SELECT * FROM mvcc;
+----+---------------------+
| id | create_at |
+----+---------------------+
| 1 | 2025-12-25 06:23:52 |
| 2 | 2025-12-25 06:23:52 |
| 3 | 2025-12-25 06:23:52 |
+----+---------------------+
テーブルを更新します。しばらく時間をおいてから(1分くらい)
-- 更新
mysql> UPDATE mvcc SET create_at=NOW() where id=1;
Query OK, 1 row affected (0.00 sec)
Rows matched: 1 Changed: 1 Warnings: 0
-- 削除
mysql> DELETE FROM mvcc WHERE id=3;
Query OK, 1 row affected (0.00 sec)
--追加
mysql> INSERT INTO mvcc VALUES ();
Query OK, 1 row affected (0.00 sec)
-- 表示
mysql> SELECT * FROM mvcc;
+----+---------------------+
| id | create_at |
+----+---------------------+
| 1 | 2025-12-25 06:27:09 |
| 2 | 2025-12-25 06:23:52 |
| 4 | 2025-12-25 06:27:21 |
+----+---------------------+
MVCCレコードの確認(TiDB関数バージョン)
前回はtikv-ctlを使ってMVCCレコードを確認しましたが、今回はTiDBの組み込み関数を使って確認してみましょう。
MVCCの情報を取得するには、まずキーを特定する必要があります。ユーザーに見えているキーは id=1 のような形式ですが、TiDB内部ではキーバリューストアであるTiKVに格納する際に、テーブルのIDを含めてクラスタ全体で一意となるようにキーを変換しています。
この一意のキーを取得する関数が、TIDB_ENCODE_RECORD_KEY です。以下のように使用します。
-- SELECT TIDB_ENCODE_RECORD_KEY(<データベース名>, <テーブル名>, <プライマリキー値>);
mysql> SELECT TIDB_ENCODE_RECORD_KEY('test', 'mvcc', 1) AS encoded_key;
+----------------------------------------+
| encoded_key |
+----------------------------------------+
| 74800000000000006e5f728000000000000001 |
+----------------------------------------+
-- 逆にデコードする関数もある
mysql> SELECT TIDB_DECODE_RECORD_KEY('74800000000000006e5f728000000000000001') AS decoded_key;
+---------------------------+
| decoded_key |
+---------------------------+
| {"id":1,"table_id":"110"} |
+---------------------------+
このキーを使って、MVCCの情報を取得します。TIDB_MVCC_INFO 関数を使用します。この関数はMVVCC情報をJSON形式で返すため、見やすくするためにJSON_PRETTY関数も使います。
mysql> SELECT JSON_PRETTY(TIDB_MVCC_INFO('74800000000000006e5f728000000000000001')) AS mvcc_info;
-- ヘッダは長いので省略
[
{
"key": "74800000000000006e5f728000000000000001",
"mvcc": {
"info": {
"writes": [
{
"commit_ts": 463106639066562562,
"short_value": "gAABAAAAAggAAAAAyWZyuBk=",
"start_ts": 463106639066562561
},
{
"commit_ts": 463106587438874627,
"short_value": "gAABAAAAAggAAAAA9GVyuBk=",
"start_ts": 463106587438874625
}
]
}
}
}
]
前回同様の出力が得られました。ここで、writes配列の各要素が、レコードの各バージョンを表しています。start_tsはトランザクションの開始タイムスタンプ、commit_tsはコミットタイムスタンプです。short_valueはレコードの内容をエンコードしたものです。
タイムスタンプは、TiDBではTSO(Timestamp Oracle)という、タイムスタンプ+論理カウンタの形式で管理されています。この情報を復元してみましょう。
TSOの復元には、TIDB_PARSE_TSO(時刻部分)とTIDB_PARSE_TSO_LOGICAL(論理カウンタ部分)を使います。
mysql> SELECT
TIDB_PARSE_TSO(463106639066562562) AS commit_time,
TIDB_PARSE_TSO_LOGICAL(463106639066562562) AS commit_logical;
+----------------------------+----------------+
| commit_time | commit_logical |
+----------------------------+----------------+
| 2025-12-25 06:27:09.740000 | 2 |
+----------------------------+----------------+
上記のように、コミットタイムスタンプから実際の時刻と論理カウンタを取得できます。
short_valueは、行の内容をBase64エンコードしたものです。このデコードはtidb-ctlの`base64decode' サブコマンドで行えます。
$ tiup ctl:v8.5.4 tidb base64decode test.mvcc gAABAAAAAggAAAAAyWZyuBk=
Starting component ctl: /Users/bohnen/.tiup/components/ctl/v8.5.4/ctl tidb base64decode test.mvcc gAABAAAAAggAAAAAyWZyuBk=
id not found in data
create_at: 2025-12-25 06:27:09
更新後のcreate_atの値が確認できました。
TiDBのGC方式について
さて、前回の記事ではGCを実行させるために、以下のような設定を行いました。
この設定について、もう少し詳しく説明します。
mysql> set config tikv gc.enable-compaction-filter = false;
TiDBのGC設定については、公式ドキュメントがあるのですが、各方式の詳細な説明はありません。
このコンパクションフィルタというのは、TiKVのストレージエンジンであるRocksDBの機能を利用したGC方式です。RocksDBはデータをSSTファイルに格納しており、定期的にコンパクション(データの再編成)を行います。この時に一緒に不要なバージョンを削除する(再編成対象から外す)のがコンパクションフィルタです。詳細は TiKVにおけるRocksDBの最適化 — MVCC GCの最適化を参照ください。
コンパクションフィルタ方式では、GCのタイミングはRocksDBのコンパクションタイミングに依存します。それでは試すことが難しくなってしまうため、前回はこの方式を無効化し、旧来のGC方式(定期的にGCを実行する方式)を使用しました。
なお、GCを手動で実行する方法は(知る限り)ありません。Issueも上がっていましたが、実装しない方針となったようです。
おわりに
今回は、前回の記事で解説しきれなかったTiDBのMVCCの詳細について説明しました。MVCCとGCについては、TiDBに限らず現代の多くのデータベースシステムで利用されています。実装を比べてみるのも面白いでしょう。