TiDB クラスタ性能ベンチ (2026-06-27)
3 ノード GMKtec M5 Ultra クラスタの TiDB v8.1.0 にどれくらい捌ける性能があるかを、point-select / OLTP read-write の 2 種類で計測したログ。
クラスタ構成は 2026-06-25_construction_plan.md の通り。本ドキュメントは「実機で何を測ったか」「どこで詰まったか」「Aurora 換算」を残す。
TL;DR
ワークロード |
数字 |
Aurora MySQL 換算 |
|---|---|---|
point-select read-only |
49k QPS, p95 4.3 ms (threads=128) |
db.r6g.2xlarge writer 相当 |
OLTP read-write mix |
1,140 TPS / 22.8k QPS, p95 100–300 ms |
db.r6g.2xlarge writer 相当 |
bulk INSERT…SELECT |
約 416k rows/sec (100 万行 / 2.4 秒) |
— |
ハードウェアは余裕があるが (バックグラウンド CPU 12%)、49k QPS は計測セットアップ上の上限。真の上限を測るにはクラスタ外の bench 専用機が必要。
物理前提
項目 |
値 |
|---|---|
ノード |
GMKtec M5 Ultra ×3 ( |
CPU |
Ryzen 7 7730U 8C/16T @ 4.5GHz |
RAM |
32 GB DDR4 |
SSD |
1 TB NVMe |
ネットワーク |
TP-Link SG3210X-M2 2.5GbE switch |
OS |
Ubuntu Server 24.04 LTS |
Kubernetes |
v1.31.14 (kubeadm + Cilium) |
TiDB |
v8.1.0 (TiDB Operator v1.6.0) |
ベンチ実行時 (2026-06-27 朝) の TiDB クラスタ初期配置:
PD node2:1 node3:2 node1:0
TiKV node2:2 node3:1 node1:0 ← 偏り
TiDB node2:1 node3:1 node1:0
node1 に control-plane taint が残っていたため Pod が node2/3 にしか乗らず、TiKV-0/-1 が両方 node2 になっていた。
計測スクリプト
すべて scripts/tidb/ 配下。
ファイル |
用途 |
|---|---|
|
|
|
|
|
高並列 (256/512) で 10k QPS 突破を狙うスイープ |
|
sysbench oltp_point_select |
|
sysbench oltp_read_write / write_only / update_index |
|
pymysql ベースの軽量 QPS 計測 (Mac から Tailscale LB に撃つ用) |
env で TIDB_HOST TIDB_PORT TIDB_USER TIDB_DB THREADS_LIST TIME を上書き可能。
実行環境のバリエーション
ロケーション |
接続先 |
1M 行 INSERT…SELECT 所要 |
|---|---|---|
Mac → kubectl port-forward 経由 |
|
28 秒 |
Mac → Tailscale LB |
|
(pybench で 10k QPS 頭打ち) |
node1 → NodePort 直 |
|
2.4 秒 |
node1 から NodePort 経由が 10 倍以上速い。port-forward は Mac↔Pi の TCP proxy が直列に乗るので latency に支配される。以後の bench は基本 node1 から実行。
point-select QPS スイープ (sysbench oltp_point_select)
TIDB_PORT=31299 TIME=15 THREADS_LIST="64 128 256" \
bash scripts/tidb/sysbench_run.sh
threads |
QPS |
p95 latency |
|---|---|---|
64 |
39,562 |
3.89 ms |
128 |
49,193 |
4.33 ms |
256 |
44,952 |
10.65 ms |
threads=256 で QPS が下がって latency が 2 倍超になっているので頭打ち。
この時のノード負荷
ベンチ中 (threads=128) の各ノード load average / sysbench プロセス:
ノード |
role |
load avg (1m) |
コメント |
|---|---|---|---|
node1 |
sysbench クライアントのみ |
0.98 / 16 |
sysbench 自体 1.55 cores |
node2 |
TiDB-0 + TiKV-0 + TiKV-1 |
4.06 / 16 |
TiKV 2 個共存で偏ってる |
node3 |
TiDB-1 + TiKV-2 + PD |
1.64 / 16 |
3 ノード合計 48 cores のうち実質 5–6 cores (12%) しか使っていない。ハードに余力ありで、49k はソフト側 (TiDB / TiKV のスケジューリング / kube-proxy 経路) の壁。
OLTP read-write mix (sysbench oltp_read_write)
TIDB_PORT=31299 TIME=20 THREADS_LIST="32 64 128 256" \
bash scripts/tidb/sysbench_rw.sh
threads |
TPS |
QPS |
p95 |
|---|---|---|---|
32 |
651 |
13,020 |
64 ms |
64 |
864 |
17,286 |
94 ms |
128 |
1,010 |
20,203 |
167 ms |
256 |
1,140 |
22,801 |
314 ms |
各 transaction = 20 queries (10 SELECT + 4 UPDATE + 4 INSERT + 2 DELETE)。
実用上の良いポイントは threads=64 の 864 TPS / 17k QPS / p95 94ms。それ以上は latency 悪化が激しい。
49k QPS の壁を破ろうとした試み
ハード余力ありでソフト側上限らしいので、SQL 層と KV 層をそれぞれ scale-out して測った。
試み 1: control-plane taint 除去 + TiDB を 2→3 replicas
kubectl taint nodes node1 node-role.kubernetes.io/control-plane:NoSchedule-
kubectl -n tidb-cluster patch tc basic --type=merge -p '{
"spec":{"tidb":{"replicas":3,
"topologySpreadConstraints":[
{"topologyKey":"kubernetes.io/hostname","maxSkew":1}
]}}}'
TiDB-2 が node1 に着地 → 3 ノード均等配置完了。再ベンチ:
threads |
QPS (TiDB=2) |
QPS (TiDB=3) |
|---|---|---|
128 |
49,193 |
41,012 |
256 |
44,952 |
41,961 |
384 |
— |
42,845 |
伸びるどころか下がった。SQL 層は壁ではなかった。
試み 2: TiKV を 3→4 replicas + topology spread
kubectl -n tidb-cluster patch tc basic --type=merge -p '{
"spec":{"tikv":{"replicas":4,
"topologySpreadConstraints":[
{"topologyKey":"kubernetes.io/hostname","maxSkew":1}
]}}}'
TiKV-3 が node1 に新規追加。PD のリージョン balance を待ってから再ベンチ:
threads |
QPS (TiKV=3) |
QPS (TiKV=4 settled) |
|---|---|---|
64 |
39,562 |
25,665 |
128 |
49,193 |
30,940 |
256 |
44,952 |
34,665 |
さらに下がった。
なぜ TiKV を増やすと下がったか
決定的なのは node1 が「bench 専用」から「bench + TiKV」になったこと。
TiKV=3 のとき: node1 は何も乗ってない → sysbench が CPU/IO を独占
TiKV=4 のとき: TiKV-3 が node1 に乗った → sysbench と TiKV が CPU/IO を奪い合い
クラスタ自体の理論上限は TiKV=4 の方が高いはずだが、計測クライアントが DB と同居した瞬間に「公正な計測」が不可能になる。これが local-cluster の根本的な計測難。
PD の状態の見方 (pd-ctl)
ベンチ中の region 分布や leader 分布を確認するときに使ったコマンド。
# エイリアス
alias pdctl='kubectl -n tidb-cluster exec -it basic-pd-0 -- /pd-ctl -u http://basic-pd:2379'
pdctl store # store 一覧 (region_count, region_size, leader_count, leader_size)
pdctl operator show # 進行中のリージョン移動 (空 = balance 完了)
pdctl scheduler show # 動いてる scheduler 一覧
pdctl config show # PD 設定 (max-replicas, schedule-limit 等)
pdctl store limit # store ごとの ops/sec 上限
region_count だけ見るとミスリードする話
TiKV-3 追加後、region_count だけ見ると激しく偏って見える:
Store |
region_count |
region_size |
leader_count |
leader_size |
|---|---|---|---|---|
TiKV-0 (node2) |
130 |
771 MB |
29 |
86 |
TiKV-1 (node2) |
121 |
779 MB |
37 |
141 |
TiKV-2 (node3) |
110 |
795 MB |
30 |
122 |
TiKV-3 (node1) |
41 |
769 MB |
38 |
689 |
region_count は 41 vs 130 で偏ってるように見えるが、region_size は全部 770–795 MB でほぼ均等。count の差は「空 region と肥大 region の混在」によるもので、実データは均衡している。
代わりに leader_size が TiKV-3 だけ 689 と突出している。これは新規追加した sbtest テーブル (大きいリージョン少数) のリーダーが TiKV-3 に偏ったため。ベンチ対象は sbtest なので、リクエストの大半が node1 に集中 → node1 の CPU/IO が sysbench と取り合い → QPS 低下。
つまり「balance してない」のではなく「balance はしてるが leader 偏在が起きてる」。
ブラウザで見るなら Grafana の PD → Statistics - balance か TiDB Dashboard の Store Topology が早い。
Aurora MySQL 換算
公開ベンチ (sysbench OLTP cached) との対比:
Aurora インスタンス |
想定 point-select QPS |
本クラスタ比 |
|---|---|---|
db.r6g.large (2 vCPU / 16GB) |
5–10k |
圧倒的に上 |
db.r6g.xlarge (4 vCPU / 32GB) |
15–25k |
上 |
db.r6g.2xlarge (8 vCPU / 64GB) |
40–60k |
ほぼ同等 |
db.r6g.4xlarge (16 vCPU / 128GB) |
80–120k |
TiDB pod を増やしてかつ別マシンから測ればここまで届きそう |
OLTP read-write の方:
Aurora インスタンス |
想定 TPS |
本クラスタ比 |
|---|---|---|
db.r6g.large |
200–300 |
上 |
db.r6g.xlarge |
500–800 |
上 |
db.r6g.2xlarge |
1,000–1,500 |
ほぼ同等 |
db.r6g.4xlarge |
2,000–3,000 |
届かず |
結論: read-only / OLTP どちらでも db.r6g.2xlarge writer 1 台相当。Aurora db.r6g.2xlarge は月 $700–800、本クラスタ (GMKtec M5 Ultra ×3 ≒ 30 万円) は約 1 年で回収できる勘定。ただし Aurora は HA・自動バックアップ・ストレージ自動拡張全部込みで、手元クラスタはそれを自分で面倒見る前提。
やってないこと / 次の宿題
クラスタ外の bench 専用機からの計測。今回は (Mac via Tailscale = 10k 頭打ち) と (node1 colocated = 49k) しか取れていない。同 LAN にもう 1 台 (別 Mini PC or Pi) 置いて測ると、真の上限が見える可能性。
TiKV / PD の rebalance を PV pin の壁を越えて実行する手順。今回は新規 TiKV を追加するだけで終わったが、本来の rebalance は scale-out → store evict → scale-in の 3 段階を踏む。
TiKV scale-out 時の
store limitチューニング。デフォルト 15 ops/sec は遅すぎる気がする。PD config (
region-schedule-limit,leader-schedule-limit) の調整で leader 偏りをどこまで早く解消できるか。Mac の Homebrew sysbench が TLS/SSL を強制してくる問題の回避。Docker 経由 or pingcap/go-tpc に切り替えるのが楽そう。
適用したクラスタ設定変更 (本検証由来)
2026-06-25_construction_plan.md 側に反映済みの変更:
Phase 3 node1 手順に control-plane taint 除去ステップを追加。これがないと TiKV/TiDB が node1 に乗らず必ず偏る。
TidbCluster CR で全コンポーネントに
topologySpreadConstraintsを追加。TiDB Operator v1.6 ではwhenUnsatisfiableフィールドは受け付けず、maxSkew+topologyKeyのみ指定する (省略するとDoNotSchedule扱い)。TiDB を 2 → 3 replicas に変更 (各ノード 1 個)。
PD / TiKV は replicas=3 のまま topology spread を入れただけだと既存 PV ノード固定で動けない点を注意書きに追加。新規構築なら最初から spread 入れて初期スケジュールで均等配置するのが正解、既存環境からの移行はちょっと面倒。
ベンチ結果ファイル
node1 上の /tmp/ 配下に残してある (再現したくなったら参照):
/tmp/sb_peak.out ← TiKV=3 時の sysbench point-select (49k)
/tmp/sb_rw.out ← OLTP read-write (1,140 TPS)
/tmp/sb_after.out ← TiDB=3 時の point-select (41k に下がった)
/tmp/sb_tikv4.out ← TiKV=4 直後の point-select (rebalance 途中)
/tmp/sb_settled.out ← TiKV=4 settled 後の point-select (依然下がってる)