TiDB AUTO_RANDOM と AUTO_INCREMENT
今日は TiDB 独自列属性の AUTO_RANDOM を見ていきます。
TiDB は MySQL 互換の分散データベースであり、NewSQL と呼ばれるカテゴリに属します。
計算処理を担う TiDB と、分散ストレージを担う TiKV によって構成されている点が大きな特徴です。
TiKV では、データは Region と呼ばれる単位で管理・保存されます。
Region は連続したキー範囲を持つ小さなデータの塊で、サイズや負荷に応じて自動的に分割(Split)され、さらに複数の TiKV ノードにレプリカとして配置されます。
この仕組みにより、特定のストレージノードに I/O が集中することを避け、
スケーラブルかつ高可用なデータ処理を実現しています。
クラウドサービスにおける「リージョン(Region)」はデータセンター群を指す用語ですが、
TiDB における Region はそれとは異なり、データ保存の最小管理単位を意味します。
デフォルトでは 1 Region あたりおよそ 96MB 程度で分割して管理されています。
AUTO_RANDOM とは
データを大量に書き込む際、特定の Region に書き込みが集中すると、
その Region を担当する TiKV ノードがホットスポットとなり、
全体のパフォーマンス低下を招く可能性があります。
このような書き込みの偏りを避けるために、主キーの値をランダム化し、
書き込み先の Region を分散させてくれる仕組みが AUTO_RANDOM です。
AUTO_RANDOM は、TiDB が提供する自動採番機能の一つで、MySQL の AUTO_INCREMENT に近い使い方ができます。ただし、AUTO_INCREMENT が単調増加する値を生成するのに対し、AUTO_RANDOM は主キーの値にランダム性を持たせて生成する点が大きく異なります。
TiDB は分散データベースであるため、主キーの値が単調に増加すると、書き込みが常に同じキー範囲に集中し、特定の Region や TiKV ノードがホットスポットになりやすくなります。AUTO_RANDOM はこの問題を避けるために、主キーの上位ビットにランダムな値を付与することで、書き込み先のキー空間を分散させます。
これにより、INSERT が複数の Region に自然に分散され、スケーラブルな書き込み性能を維持しやすくなります。
さっそくやってみる
では AUTO_RANDOM属性を持つテーブルを作成してみます。
CREATE TABLE users (
id BIGINT PRIMARY KEY AUTO_RANDOM,
name VARCHAR(100)
);
AUTO_RANDOM は BIGINT 型の主キーにのみ指定可能で、主キーの先頭列でなければなりません。SQLの実行結果が以下の様にWarningとなりますがこれはドキュメントにも記載されている正常な動作になります。

次に10件データを挿入してみます。
INSERT INTO users (name) VALUES
('Alice'),
('Bob'),
('Carol'),
('Dave'),
('Eve'),
('Frank'),
('Grace'),
('Heidi'),
('Ivan'),
('Judy');
このように値が自動で採番されています。

TiDB Dedicated だとSHOW TABLE users REGIONS;でRegionとデータの分散を確認できるのですが、Serverlessだと実行が禁止されているようです。
'SHOW TABLE REGIONS' is not supported on TiDB Serverless, see https://docs.pingcap.com/tidbcloud/limited-sql-features#statements for more details.
明示的な値の挿入
AUTO_INCREMENTと同様にAUTO_RANDOM列にも明示的な値の挿入が可能です。
SET @@allow_auto_random_explicit_insert = 1;で環境変数を有効化することで、以下のSQLが実行可能になります。
INSERT INTO users (id, name)
VALUES (1234567890123, 'ExplicitUser');

自動採番列に明示的な値を挿入することは重複や数字の整合性が懸念されるため、通常のデータ挿入には行いませんが、この通り桁数などを意図的に変更させることで重複を防ぐことができます。
以下の様なケースに有効です。
1.外部データの移行 データ移行を行う際、既存システムで ID が外部参照されているケースでは前のIDを保持したいケースがあります。
2.外部システムと ID を同期している場合 自動採番される通常のデータとは別に外部システム連携用データを同居させたい場合この方式は有効です。メッセージキューやイベントから発番される ID を埋め込むなどの場合有効です。
ただしいずれの場合でも、通常運用で挿入されるデータは自動採番を用いて外部参照を行わない設計を考慮したほうがよさそうです。
シャードビット数の指定
以下のSQL書式で自動採番におけるシャードビット数の桁数を増やすことが可能です。
CREATE TABLE users2 (
id BIGINT PRIMARY KEY AUTO_RANDOM(5),
name VARCHAR(100)
);
AUTO_RANDOM のID(64bit)は概ね次に3つから構成されています。
符号:1ビット
shard bits(Sビット):ランダムに散らすための部分
auto-increment bits:単調増加する部分(shard bits の下位側)(64-1-S)
shard bitsは1から15の値で指定できますが、shard bitsを増やせば増やすほどauto-increment bitsの空間が狭くなるというトレードオフの関係にあります。
単調増加ID(AUTO_INCREMENT)は末尾キーに書き込みが寄りやすく、特定の Region / TiKV がホットになりがちです。AUTO_RANDOM はそれを避けるための仕組みで、S を増やすほど分散の“粒度”が増します。その一方で、64bit の中で shard bits に S ビットを割くぶん、auto-increment に使えるビット数が減ります。つまり、生成できるIDの上限(最大件数)が小さくなります。
TiDBでは、とあるシャードがいっぱいでデータを保存できないときに、別のシャードにデータを保存する、という動作をしないようでFailed to read auto-increment value from storage engineエラーとなります。このため、auto-increment bitsには十分な空間を用意しておく必要があります。デフォルトではshard bitsは5bitとなり、auto-increment bitsは58bitです。
288,230,376,151,711,744(約288京)個ですので、まず心配の必要はない空間が用意されています。
符号付きビットのないAUTO_RANDOMという別の仕組みも用意されていますので、詳しくはこちらを参照ください。

まとめ:AUTO_RANDOMの使用判断
AUTO_INCREMENTをつかうかAUTO_RANDOMを使うかは一般的に以下で整理できそうです。
使うべきケース:
- 大量の書き込みが発生するテーブル
- 時系列データなど、IDが単調増加しやすいケース
使わないべきケース:
- IDの順序性が重要なケース(例: 注文番号など)
- 小規模なテーブル(ホットスポットの心配がない)
- 既存のアプリケーションコードがIDの連続性に依存している場合
Discussion