1
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?

TiDBのMVCCを理解する(1)

Posted at

はじめに

TiDBはMVCCを採用しており、テーブルのレコードの変更は削除も含めてすべて保存されています。
実際にSELECTする際は、この履歴から最新のものを取得しています。

この動作は当然更新量が多くなればデータベースのパフォーマンスに影響を与えます。そのようなTiDBのMVCCのトラブルシュートについては下記の記事が詳しいです。

本記事では、TiDB Playground を使って、もっとプリミティブなレベルでTiDBのMVCCの挙動を理解していきます。

TiDB Playgroundの使い方については、私の過去記事を御覧ください。

超ざっくりとしたMVCCの解説

TiDBのMVCCは

  • コミットした更新を{トランザクション開始, トランザクション終了}のタイムスタンプをつけて記録している
  • Deleteしたレコードも削除マーカー(tombstoneとよばれる)をつけて記録する
  • GCと呼ばれる操作により、古い履歴は定期的に削除される

という動きをします。

この動きを見ていきましょう。そのための手順として、

  1. テーブルを作成して、レコードをいくつか挿入
  2. レコードを更新、削除する
  3. MVCC履歴の確認
  4. GCが発生したのち、MVCC履歴の確認(削除されているはず)

をやっていきます。

TiDBのGCはデフォルト10分間隔なので、このハンズオンには20分くらいかかります。お時間のあるときにやってみてください。

テーブルの作成と更新

まず TiDB Playgroundを起動し、mysqlクライアントで接続してください。

> 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> CREATE TABLE mvcc(id INT AUTO_INCREMENT PRIMARY KEY, create_at DATETIME DEFAULT CURRENT_TIMESTAMP);
Query OK, 0 rows affected (0.03 sec)

テーブルの作成

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-16 09:35:14 |
|  2 | 2025-12-16 09:35:14 |
|  3 | 2025-12-16 09:35:14 |
+----+---------------------+

テーブルを更新します。しばらく時間をおいてから(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-16 09:36:25 |
|  2 | 2025-12-16 09:35:14 |
|  4 | 2025-12-16 09:37:47 |
+----+---------------------+

MVCCレコードの確認

MVCCレコードの確認には、tidb-ctlコマンドを利用します。別のコンソールを開き、tiup コマンドのサブコマンドを利用します。

tidb-ctlのオプションは下記です。tiupのサブコマンドtidbとして実行します。

tiup ctl:<バージョン> tidb mvcc key -d <データベース名> -t <テーブル名> -i <PKの値>
> tiup ctl:v8.5.4 tidb mvcc key -d test -t mvcc -i 1
{
    "key": "74800000000000007D5F728000000000000001",
    "region_id": 122,
    "value": {
        "info": {
            "writes": [
                {
                    "start_ts": 462905772701122561,
                    "commit_ts": 462905772701122562,
                    "short_value": "gAABAAAAAggAAAAAGZlguBk="
                },
                {
                    "start_ts": 462905754141327362,
                    "commit_ts": 462905754141327364,
                    "short_value": "gAABAAAAAggAAAAAzphguBk="
                }
            ]
        }
    }
}

id=1のMVCC履歴が表示されました。更新したので2つエントリがあります。ここで start_tsはトランザクションの開始タイムスタンプ(TSO)、commit_tsはコミット時のタイムスタンプです。short_valueはその時の値を示しています。

これらの情報はそれぞれエンコードされており、デコード可能なのですが本記事では扱いません。次回以降に確認していきます。

同様に、削除された id=3もみてみましょう。

> tiup ctl:v8.5.4 tidb mvcc key -d test -t mvcc -i 3
{
    "key": "74800000000000007D5F728000000000000003",
    "region_id": 122,
    "value": {
        "info": {
            "writes": [
                {
                    "type": 1,
                    "start_ts": 462905784248565762,
                    "commit_ts": 462905784248565763
                },
                {
                    "start_ts": 462905754141327362,
                    "commit_ts": 462905754141327364,
                    "short_value": "gAABAAAAAggAAAAAzphguBk="
                }
            ]
        }
    }
}

先ほどと異なり、最初のMVCCエントリに "type":1という属性がありますね。これが削除マーカーです。削除されたことを示すエントリなので、short_valueはありません。

他のキーについても試してみてください。

ステイル読み取り(タイムトラベルクエリ)を試してみる

さて、履歴があることで、実は過去時点の断面を取り出すことが可能です。やってみましょう。
最初の断面を取り出すには、SELECT ... AS OF TIMESTAMP を利用します。

mysql> SELECT * FROM mvcc AS OF TIMESTAMP '2025-12-16 09:35:15';
+----+---------------------+
| id | create_at           |
+----+---------------------+
|  1 | 2025-12-16 09:35:14 |
|  2 | 2025-12-16 09:35:14 |
|  3 | 2025-12-16 09:35:14 |
+----+---------------------+

更新されたレコードや、追加されたid=4のレコードがありませんね。create_atも揃っています。
途中断面ではどうでしょうか?

mysql> SELECT * FROM mvcc AS OF TIMESTAMP '2025-12-16 09:36:26';
+----+---------------------+
| id | create_at           |
+----+---------------------+
|  1 | 2025-12-16 09:36:25 |
|  2 | 2025-12-16 09:35:14 |
|  3 | 2025-12-16 09:35:14 |
+----+---------------------+

最初の(id=1)更新のみが取得できています。id=3のDELETEや、id=4の追加はまだされていない状態です。

GCを実行する

TiDBではGCが定期的に実行されているのですが、最新のTiDBではGCが効率化されており、そのままでは古い履歴は削除されません。ここではあえて非効率なオプションを有効にして、実際に削除されるのを見てみましょう。

mysql> set config tikv gc.enable-compaction-filter = false;
Query OK, 0 rows affected (0.02 sec)

このオプションを設定しないと、TiDB PlaygroundではGCが発生してもMVCCエントリが削除されないので気をつけてください。
設定したオプションの解説はこのシリーズの別の記事で解説します。TiDB Cloudでは設定できません。

実際にGCが実行されたタイミングは、下記のSQLでわかります。

mysql> PAGER less -S
mysql> SELECT * FROM tidb;

+--------------------------+-------------------------------------------------------------------------------------------+------------------------------------->
| VARIABLE_NAME            | VARIABLE_VALUE                                                                            | COMMENT                             >
+--------------------------+-------------------------------------------------------------------------------------------+------------------------------------->
| bootstrapped             | True                                                                                      | Bootstrap flag. Do not delete.      >
| tidb_server_version      | 220                                                                                       | Bootstrap version. Do not delete.   >
| system_tz                | Asia/Tokyo                                                                                | TiDB Global System Timezone.        >
| new_collation_enabled    | True                                                                                      | If the new collations are enabled. D>
| ddl_table_version        | 4                                                                                         | DDL Table Version. Do not delete.   >
| tikv_gc_leader_uuid      | 66c7e0171c40011                                                                           | Current GC worker leader UUID. (DO N>
| tikv_gc_leader_desc      | host:jgray.local, pid:38806, start at 2025-12-15 09:54:24.667749 +0900 JST m=+3.677934709 | Host name and pid of current GC lead>
| tikv_gc_leader_lease     | 20251216-09:51:37.617 +0900                                                               | Current GC worker leader lease. (DO >
| tikv_gc_auto_concurrency | true                                                                                      | Let TiDB pick the concurrency automa>
| tikv_gc_enable           | true                                                                                      | Current GC enable status            >
| tikv_gc_run_interval     | 10m0s                                                                                     | GC run interval, at least 10m, in Go>
| tikv_gc_life_time        | 10m0s                                                                                     | All versions within life time will n>
| tikv_gc_last_run_time    | 20251216-09:47:37.598 +0900                                                               | The time when last GC starts. (DO NO>
| tikv_gc_safe_point       | 20251216-09:37:37.598 +0900                                                               | All versions after safe point can be>
| tidb_stats_gc_last_ts    | 462905799500365824                                                                        | NULL                                >
| tikv_gc_mode             | distributed                                                                               | Mode of GC, "central" or "distribute>
+--------------------------+-------------------------------------------------------------------------------------------+------------------------------------->

ここで表示されている、tikv_gc_last_run_timeにGCが実行され、tikv_gc_safe_point以前の履歴レコードが削除されます。

GCは10分間隔で実行されるため、次の実行タイミングはtikv_gc_last_run_timeのおよそ10分後です。もしレコードの作成前にGCが実行されていた場合は、次のGCタイミングを待ってください。

MVCCレコードが削除されていることを確認

MVCCレコードが削除されていることは、ステイル読み取りを実行することでわかります。ステイル読み取りで指定する時間は、tikv_gc_safe_pointより後を指定する必要があります。

mysql> SELECT * FROM mvcc AS OF TIMESTAMP '2025-12-16 09:36:26';
ERROR 9006 (HY000): GC life time is shorter than transaction duration, transaction starts at 2025-12-16 09:36:26 +0900 JST, GC safe point is 2025-12-16 09:37:37.598 +0900 JST

また、tidb-ctl でも同様に確認できます。

❯ tiup ctl:v8.5.4 tidb mvcc key -d test -t mvcc -i 1
Starting component ctl: /Users/bohnen/.tiup/components/ctl/v8.5.4/ctl tidb mvcc key -d test -t mvcc -i 1
{
    "key": "74800000000000007D5F728000000000000001",
    "region_id": 122,
    "value": {
        "info": {
            "writes": [
                {
                    "start_ts": 462905772701122561,
                    "commit_ts": 462905772701122562,
                    "short_value": "gAABAAAAAggAAAAAGZlguBk="
                }
            ]
        }
    }
}

# 削除された場合はinfoが空になる
❯ tiup ctl:v8.5.4 tidb mvcc key -d test -t mvcc -i 3
Starting component ctl: /Users/bohnen/.tiup/components/ctl/v8.5.4/ctl tidb mvcc key -d test -t mvcc -i 3
{
    "key": "74800000000000007D5F728000000000000003",
    "region_id": 122,
    "value": {
        "info": {}
    }
}

まとめ

この記事では、TiDBの実際のMVCCレコードを確認することで、MVCCの履歴の持ち方やGCによる削除の様子を確認しました。MVCCの動きが具体的に理解できるのではないかと思います。

次回は、ここの動きをおさらいして、より詳細に動きを紹介していきたいと思います。

1
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
1
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?