【図解】TiDBのAUTO_INCREMENTを体系的に理解する

DB

はじめに

TiDBはクラスタ化された分散ノードが協調動作することでスケーラビリティを実現しています。

一方でスケーラビリティを重視してノードを分散させるようにしたことで、デフォルトのAUTO_INCREMENTの動作ではIDは1, 30001, 2, 30002のように値が飛び飛びで採番されます。

本記事では分散SQLであるTiDBにおけるauto_incrementの設定・動作例・懸念点を整理した上で、実際にローカルでTiDBを動かしながら検証を行います。

TiDBのAUTO_INCREMENTの概要

TiDBのauto_incrementの基本的な概念は以下の公式ドキュメントで説明されていますが、ややこしいので改めて整理します。

AUTO_INCREMENT
TiDB の AUTO_INCREMENT` 列属性について学習します。

TiDBのAUTO_INCREMENTは主に以下の3種類存在します。

  1. AUTO_INCREMENT(デフォルト)
  2. AUTO_INCREMENT(AUTO_ID_CACHE 2以上を設定)
  3. AUTO_INCREMENT(AUTO_ID_CACHE 1設定, 通称MySQL互換モード)

AUTO_INCREMENT(デフォルト)

デフォルトのAUTO_INCREMENTではクラスタ内に立ち上げているそれぞれのTiDBノードに3万ずつのIDのキャッシュが割り振られます。

Node 1: 1 ~ 30,000
Node 2: 30,001 ~ 60,000

3万という数値はautoid.goにてvar defaultStep = int64(30000)として定義されていることを確認できます。
https://github.com/pingcap/tidb/blob/bce559b421ad7436c05f61f6222e11cb6c01adbe/pkg/meta/autoid/autoid.go#L281

INSERTの実行時にアクセスしたTiDBノードが保有している採番帯に応じて番号が割り振られていくため、IDは単調増加しません。

例えばNode1とNode2に交互にINSERTがリクエストされた場合、以下のように番号が割り振られます。

1
30001
2
30002

またTiDB Serverが再起動した場合は、キャッシュを破棄して未使用のIDを3万だけ確保するので、番号が更に飛びます。

1
30001
2
30002

~ failover ~ 
3
60001

AUTO_INCREMENT(AUTO_ID_CACHE 2以上を設定)

TiDBのAUTO_INCREMENTにはAUTO_ID_CACHEというパラメータが存在しており、テーブルを作成する際にノードにキャッシュするIDの数を指定できます。

CREATE TABLE t1 (
    id BIGINT PRIMARY KEY AUTO_INCREMENT,
    created_at DATETIME NOT NULL DEFAULT NOW()
) AUTO_ID_CACHE = 100;

ここでAUTO_ID_CACHE 100と指定した場合、各TiDB Serverノードには100ずつのIDがキャッシュされるようになります。

Node 1: 1 ~ 100
Node 2; 101 ~ 200

2ノード存在するTiDB Serverに交互にINSERTが実行された場合は以下のようにIDが割り振られます。

1,
101,
2,
102

またTiDB Serverが再起動した場合はすでに確保したキャッシュを破棄して新たにIDをAUTO_ID_CACHEの指定するだけキャッシュします。

1,
101,
2,
102,

~ failover ~

201,
3,
202,
4

AUTO_INCREMENT(AUTO_ID_CACHE 1設定, 通称MySQL互換モード)

最後に他の設定値と異なる挙動を見せるのが、AUTO_ID_CACHE 1の設定です。

この設定では1つのTiDB Serverが採番ノードに選ばれて、そのノードに4000だけIDがキャッシュされます。

単一のノードが採番ノードとして選択されている処理は、autoid.goのNewAllocator()で利用されているnewSinglePointAlloc()というメソッドにて確認できます。
https://github.com/pingcap/tidb/blob/bce559b421ad7436c05f61f6222e11cb6c01adbe/pkg/meta/autoid/autoid.go#L625

INSERT時はどのノードにリクエストが飛んできても採番ノードを経由して採番するようになるため、IDは単調増加します。

1
2
3
4

またTiDBサーバが再起動した場合はキャッシュを破棄して、再度4000だけIDを確保するため、それだけIDが飛びます。

1
2
3
4

~ fail over ~

4001
4002

なお、この4000という謎の数についてはautoid.goのbatchという変数にて確認できます。SAさん曰く適当に決めているんじゃないかとのことです。

const batch = 4000
https://github.com/pingcap/tidb/blob/bce559b421ad7436c05f61f6222e11cb6c01adbe/pkg/autoid_service/autoid.go#L422-L423

単調増加性が確保されるのは良い点ですが、採番オーナーとなるノードはTiDB Clusterで1つだけとなるため、採番オーナーとなるTiDB Serverが性能上のボトルネックになります。

PingCapのSAさんによるとv8.5時点では異なるテーブル・データベースでAUTO_ID_CACHEを設定しても採番オーナーとなるTiDB Serverはクラスタで1つになるとのことでした。

AUTO_ID_CACHEを乱用すると分散DBであるメリットが得られなくなるため、注意を要する設定と言えるでしょう。

AUTO_INCREMENT設定のまとめ

採番ノードの数キャッシュする番号の数TiDB Serevr再起動時の番号飛び単調増加性性能懸念
デフォルトTiDB Serverノード数に一致各ノードに3万ずつ3万なし
AUTO_ID_CACHE 11つ採番ノードに40004000あり採番オーナとなるTiDB Serverがボトルネックになる
AUTO_ID_CACHE 2以上TiDB Serverノード数に一致各ノードにAUTO_ID_CACHE設定値だけAUTO_ID_CACHE設定値だけなし

検証概要

検証の概要としては、全く同じ構成のテーブルに以下のAUTO_INCREMENT設定を入れた上で、そのテーブルにINSERTを実行することでIDの挙動を確認します。

  1. デフォルト動作
  2. AUTO_ID_CACHE 1
  3. AUTO_ID_CACHE 2
  4. AUTO_ID_CACHE 0

またINSERTの実行シナリオは以下の通りです。

  1. TiDB Server port=4000, 4001に交互にinsertを実行
  2. TiDB Server再起動後にinsertを実行

検証環境

  • OS: Ubuntu 24.04.2 LTS
  • TiDB: v8.5.3

TiDB環境準備

Clusterの立ち上げ


まずはtiup playgroundでクラスターをローカルに立ち上げます。

$ tiup playground \
--tag auto_increment \
--db 2 \
--kv 1 \
--pd 1

Checking updates for component playground...
A new version of playground is available: v1.16.1 -> v1.16.4

    To update this component:   tiup update playground
    To update all components:   tiup update --all


Note: Version constraint  is resolved to v8.5.3. If you'd like to use other versions:

    Use exact version:      tiup playground v7.1.0
    Use version range:      tiup playground ^5
    Use nightly:            tiup playground nightly

Start pd instance: v8.5.3
Start tikv instance: v8.5.3
Start tidb instance: v8.5.3
Start tidb instance: v8.5.3
Waiting for tidb instances ready
127.0.0.1:4000 ... Done
127.0.0.1:4001 ... Done
Start tiflash instance: v8.5.3
Waiting for tiflash instances ready
127.0.0.1:3930 ... Done

🎉 TiDB Playground Cluster is started, enjoy!

Connect TiDB:    mysql --comments --host 127.0.0.1 --port 4001 -u root
Connect TiDB:    mysql --comments --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

テーブル作成

次にtestデータベースに以下のテーブルを作成します。

  • td: デフォルトのAUTO_INCREMENTを設定
  • t0: AUTO_ID_CACHE0を設定
  • t1; AUTO_ID_CACHE1を設定
  • t2: AUTO_ID_CACHE2を設定

-- Default AUTO_INCREMENT behavior
DROP TABLE IF EXISTS td;
CREATE TABLE td (
    id BIGINT PRIMARY KEY AUTO_INCREMENT,
    created_at DATETIME NOT NULL DEFAULT NOW()
);

-- AUTO_ID_CACHE = 0
DROP TABLE IF EXISTS t0;
CREATE TABLE t0 (
    id BIGINT PRIMARY KEY AUTO_INCREMENT,
    created_at DATETIME NOT NULL DEFAULT NOW()
) AUTO_ID_CACHE = 0;

-- AUTO_ID_CACHE = 1
DROP TABLE IF EXISTS t1;
CREATE TABLE t1 (
    id BIGINT PRIMARY KEY AUTO_INCREMENT,
    created_at DATETIME NOT NULL DEFAULT NOW()
) AUTO_ID_CACHE = 1;

-- AUTO_ID_CACHE = 2
DROP TABLE IF EXISTS t2;
CREATE TABLE t2 (
    id BIGINT PRIMARY KEY AUTO_INCREMENT,
    created_at DATETIME NOT NULL DEFAULT NOW()
) AUTO_ID_CACHE = 2;

デフォルト動作

TiDB Server port=4000, 4001に交互にinsertを実行

-- 4000port
mysql> INSERT INTO td VALUES ();

-- 4001port
mysql> INSERT INTO td VALUES ();

-- 4000port
mysql> INSERT INTO td VALUES ();

-- 4001port
mysql> INSERT INTO td VALUES ();

mysql> select * from td order by created_at asc;
+-------+---------------------+
| id    | created_at          |
+-------+---------------------+
|     1 | 2025-11-03 16:43:27 |
| 30001 | 2025-11-03 16:43:41 |
|     2 | 2025-11-03 16:43:45 |
| 30002 | 2025-11-03 16:43:47 |
+-------+---------------------+
4 rows in set (0.00 sec)

各ノードに3万ずつ番号がキャッシュされており、どのノードにINSERTがリクエストされるかによって割り振られる番号が異なっていることがわかります。

TiDB Server再起動後にinsertを実行

次はTiDB Serverを再起動した後にINSERTを実行してみます。

-- Ctrl-Cを押してplaygroundを停止後に再度同じtagで起動する

-- 4000port
mysql> INSERT INTO td VALUES ();

-- 4001port
mysql> INSERT INTO td VALUES ();

-- 4000port
mysql> INSERT INTO td VALUES ();

-- 4001port
mysql> INSERT INTO td VALUES ();

selectして結果を確認してみると、3万ずつキャッシュされたIDが破棄されて新たにキャッシュしたIDから番号が割り振られていることがわかります。

mysql> select * from td order by created_at;
+-------+---------------------+
| id    | created_at          |
+-------+---------------------+
|     1 | 2025-11-03 16:43:27 |
| 30001 | 2025-11-03 16:43:41 |
|     2 | 2025-11-03 16:43:45 |
| 30002 | 2025-11-03 16:43:47 |
| 60001 | 2025-11-03 16:49:42 |
| 90001 | 2025-11-03 16:49:52 |
| 60002 | 2025-11-03 16:50:07 |
| 90002 | 2025-11-03 16:50:11 |
+-------+---------------------+
8 rows in set (0.00 sec)

AUTO_ID_CACHE 1(MySQL互換モード)の動作

次にAUTO_ID_CACHE 1の動作を確認しましょう。

TiDB Server port=4000, 4001に交互にinsertを実行

AUTO_ID_CACHE 1を設定したt1にinsertを実行していきます。

-- 4000port
mysql> INSERT INTO t1 VALUES ();

-- 4001port
mysql> INSERT INTO t1 VALUES ();

-- 4000port
mysql> INSERT INTO t1 VALUES ();

-- 4001port
mysql> INSERT INTO t1 VALUES ();

結果を確認するとIDが単調増加していることがわかります。

mysql> select * from t1 order by created_at asc;
+----+---------------------+
| id | created_at          |
+----+---------------------+
|  1 | 2025-11-03 16:53:20 |
|  2 | 2025-11-03 16:53:28 |
|  3 | 2025-11-03 16:53:31 |
|  4 | 2025-11-03 16:53:34 |
+----+---------------------+
4 rows in set (0.00 sec)

TiDB Server再起動後にinsertを実行

こちらも同様にTiDBを再起動してinsertを実行してみます。

-- 4000port
mysql> INSERT INTO t1 VALUES ();

-- 4001port
mysql> INSERT INTO t1 VALUES ();

-- 4000port
mysql> INSERT INTO t1 VALUES ();

-- 4001port
mysql> INSERT INTO t1 VALUES ();

結果を確認すると4000番だけIDが飛びますが、その後は値が単調増加していることがわかります。

mysql> select * from t1 order by created_at asc;
+------+---------------------+
| id   | created_at          |
+------+---------------------+
|    1 | 2025-11-03 16:53:20 |
|    2 | 2025-11-03 16:53:28 |
|    3 | 2025-11-03 16:53:31 |
|    4 | 2025-11-03 16:53:34 |
| 4001 | 2025-11-03 16:57:07 |
| 4002 | 2025-11-03 16:57:13 |
| 4003 | 2025-11-03 16:57:29 |
| 4004 | 2025-11-03 16:57:32 |
+------+---------------------+
8 rows in set (0.00 sec)

AUTO_ID_CACHE 2の動作

TiDB Server port=4000, 4001に交互にinsertを実行

-- 4000port
mysql> INSERT INTO t2 VALUES ();

-- 4001port
mysql> INSERT INTO t2 VALUES ();

-- 4000port
mysql> INSERT INTO t2 VALUES ();

-- 4001port
mysql> INSERT INTO t2 VALUES ();

-- 4000port
mysql> INSERT INTO t2 VALUES ();

-- 4001port
mysql> INSERT INTO t2 VALUES ();
mysql> INSERT INTO t2 VALUES ();
mysql> INSERT INTO t2 VALUES ();

結果を確認すると、ノードごとに交互に値が割り振られていることがわかります。

mysql> select * from t2 order by created_at asc;
+----+---------------------+
| id | created_at          |
+----+---------------------+
|  1 | 2025-11-03 16:59:42 |
|  3 | 2025-11-03 16:59:45 |
|  2 | 2025-11-03 16:59:49 |
|  4 | 2025-11-03 16:59:54 |
|  5 | 2025-11-03 17:01:03 |
|  7 | 2025-11-03 17:01:06 |
|  8 | 2025-11-03 17:02:21 |
|  9 | 2025-11-03 17:02:23 |
| 10 | 2025-11-03 17:02:24 |
+----+---------------------+
9 rows in set (0.00 sec)

再起動後

-- 4000port
mysql> INSERT INTO t2 VALUES ();

-- 4001port
mysql> INSERT INTO t2 VALUES ();

-- 4000port
mysql> INSERT INTO t2 VALUES ();

-- 4001port
mysql> INSERT INTO t2 VALUES ();

こちらは再起動しても特に大きな変化はありませんでした。

mysql> select * from t2 order by created_at asc;
+----+---------------------+
| id | created_at          |
+----+---------------------+
|  1 | 2025-11-03 16:59:42 |
|  3 | 2025-11-03 16:59:45 |
|  2 | 2025-11-03 16:59:49 |
|  4 | 2025-11-03 16:59:54 |
|  5 | 2025-11-03 17:01:03 |
|  7 | 2025-11-03 17:01:06 |
|  8 | 2025-11-03 17:02:21 |
|  9 | 2025-11-03 17:02:23 |
| 10 | 2025-11-03 17:02:24 |
| 11 | 2025-11-03 17:04:14 | -- 再起動後
| 13 | 2025-11-03 17:04:19 |
| 12 | 2025-11-03 17:04:26 |
| 14 | 2025-11-03 17:04:28 |
+----+---------------------+
13 rows in set (0.00 sec)

AUTO_ID_CACHE 0の動作

AUTO_ID_CACHE 0の際の動作はデフォルトのAUTO_INCREMENTと全く変わりませんでした。

TiDB Server port=4000, 4001に交互にinsertを実行

-- 4000port
mysql> INSERT INTO t0 VALUES ();

-- 4001port
mysql> INSERT INTO t0 VALUES ();

-- 4000port
mysql> INSERT INTO t0 VALUES ();

-- 4001port
mysql> INSERT INTO t0 VALUES ();

mysql> select * from t0 order by created_at asc;
+-------+---------------------+
| id    | created_at          |
+-------+---------------------+
|     1 | 2025-11-03 17:06:36 |
| 30001 | 2025-11-03 17:06:42 |
|     2 | 2025-11-03 17:06:45 |
| 30002 | 2025-11-03 17:06:49 |
+-------+---------------------+
4 rows in set (0.00 sec)

再起動後

-- 4000port
mysql> INSERT INTO t0 VALUES ();

-- 4001port
mysql> INSERT INTO t0 VALUES ();

-- 4000port
mysql> INSERT INTO t0 VALUES ();

-- 4001port
mysql> INSERT INTO t0 VALUES ();

mysql> select * from t0 order by created_at asc;
+-------+---------------------+
| id    | created_at          |
+-------+---------------------+
|     1 | 2025-11-03 17:06:36 |
| 30001 | 2025-11-03 17:06:42 |
|     2 | 2025-11-03 17:06:45 |
| 30002 | 2025-11-03 17:06:49 |
| 60001 | 2025-11-03 17:08:05 |
| 90001 | 2025-11-03 17:08:08 |
| 60002 | 2025-11-03 17:08:11 |
| 90002 | 2025-11-03 17:08:13 |
+-------+---------------------+
8 rows in set (0.01 sec)

検証まとめ

単調増加性ノードにキャッシュされるIDの数TiDB Server再起動時の挙動動作例
デフォルトなし各TiDB Serverに30,000ずつ古いキャッシュを破棄して30,000ずつ再度キャッシュする1
30001
2
30002
~ 再起動 ~
60001
3
60002
4
AUTO_ID_CACHE 0同上同上同上同上
AUTO_ID_CACHE 1あり1つのTiDB Serverに4000ずつ古いキャッシュを破棄して再度4,000キャッシュする1
2
3
4
~ 再起動 ~
4001
4002
AUTO_ID_CACHE 2なし各TiDB Serverに2ずつ古いキャッシュを破棄して再度2ずつキャッシュする1
3
2
4
~ 再起動 ~
5
7
6
8

タイトルとURLをコピーしました