DROP TABLE 後も TiKV のメモリ使用量が下がらない件の調査と解放手順
観測日: 2026-07-03
対象: 検証用 self-hosted TiDB クラスタ (
basic, tidb-cluster namespace)関連: 2026-07-01 LOAD DATA 大容量ファイル調査,
cluster/manifests/tidb-cluster/tidb-cluster.yaml
症状
tidb-seeder で 500 万行 (約 33GB) を LOAD DATA した検証用テーブルを DROP したのに、TiKV 3 台のメモリ使用量が 8〜9 GiB のまま下がらない。
結論
データ自体は GC・region マージまで完了して消えている。メモリの高止まりは TiKV (Rust) のブロックキャッシュと jemalloc の設計によるもので、リークではない。
RocksDB のブロックキャッシュは LRU で、
storage.block-cache.capacity(本クラスタは 4GB)まで一度埋まると DROP TABLE ではパージされず、自発的に縮むこともないjemalloc は free 済みページをすぐ OS に返さない(retained ≈ 8GB を実測)
cgroup のメモリ使用量にはページキャッシュ(inactive_file 1〜2.4GB)も含まれ、実態より大きく見える。この分はメモリ圧迫時にカーネルが自動回収する
limit 12Gi に対して 9GiB なので放置しても実害はない。リセットしたい場合は後述の rolling restart か block cache の動的縮小を行う。
調査手順
1. Pod 実メモリと anon / page cache の内訳
for p in basic-tikv-0 basic-tikv-1 basic-tikv-2; do
echo "=== $p ==="
kubectl exec -n tidb-cluster $p -- sh -c '
cat /sys/fs/cgroup/memory.current
grep -E "^(anon|file|inactive_file) " /sys/fs/cgroup/memory.stat'
done
実測 (2026-07-03):
Pod |
memory.current |
anon |
file (page cache) |
|---|---|---|---|
basic-tikv-0 |
9214 MiB |
6619 MiB |
2460 MiB |
basic-tikv-1 |
9060 MiB |
6678 MiB |
2256 MiB |
basic-tikv-2 |
7947 MiB |
6704 MiB |
1148 MiB |
2. TiKV 内部のメモリ内訳 (status port :20180 の /metrics)
kubectl exec -n tidb-cluster basic-tikv-0 -- sh -c \
'curl -s http://127.0.0.1:20180/metrics | grep -E "^tikv_engine_block_cache_size_bytes|^process_resident_memory_bytes|^tikv_allocator_stats"'
実測 (basic-tikv-0):
指標 |
値 |
意味 |
|---|---|---|
process_resident_memory_bytes |
7.28 GB |
プロセス RSS |
tikv_engine_block_cache_size_bytes |
4.28 GB |
ブロックキャッシュ。設定上限 4GB に張り付き |
tikv_allocator_stats{type=allocated} |
6.26 GB |
jemalloc 経由で実際に使用中 |
tikv_allocator_stats{type=fragmentation} |
0.8 GB |
断片化で OS に返っていない分 |
tikv_allocator_stats{type=retained} |
8.03 GB |
jemalloc が解放後も保持しているマップ済み領域 |
3. DROP したデータが実際に消えているかの確認 (PD)
kubectl exec -n tidb-cluster basic-pd-0 -- sh -c '
/pd-ctl region --jq=".count"
curl -s http://127.0.0.1:2379/pd/api/v1/stores | grep -E "region_count|region_size|leader_count"'
pd-ctl regionの出力は{"count": N, "regions": [...]}形式。--jq="length"はトップレベルのキー数(常に 2)を返すので region 数には使えない。region 数は.count、リーダー偏りは stores 側のleader_countで見る(region_countはレプリカ数なので全 store で揃い、偏り判定には使えない)。
実測: region はクラスタ全体で 5 個 / 約 24MB。500 万行分の region は GC・マージ済みで存在しない。
ディスク使用量 (
df -h /var/lib/tikvで 39〜45GB) は local-path が OS ディスク共用のため、TiKV データではなく OS + コンテナイメージ等を含んだ値。
対処
A. TiKV rolling restart(メモリを完全リセット)
3 レプリカ構成なので 1 台ずつなら安全。必ず Ready を待ってから次に進む。
for i in 0 1 2; do
kubectl -n tidb-cluster delete pod basic-tikv-$i
kubectl -n tidb-cluster wait --for=condition=Ready pod/basic-tikv-$i --timeout=10m
done
再起動後の確認:
for p in basic-tikv-0 basic-tikv-1 basic-tikv-2; do
v=$(kubectl exec -n tidb-cluster $p -- cat /sys/fs/cgroup/memory.current)
echo "$p: $((v/1024/1024)) MiB"
done
実測 (2026-07-03 再起動後): 3 台とも 8〜9 GiB → 3.7〜3.9 GiB に低下。
Pod |
再起動前 |
再起動後 |
|---|---|---|
basic-tikv-0 |
9214 MiB |
3917 MiB |
basic-tikv-1 |
9060 MiB |
3733 MiB |
basic-tikv-2 |
7947 MiB |
3911 MiB |
ベンチを再開すればブロックキャッシュはまた上限 4GB まで積み上がる。恒久的に下げたいなら B を使う。
再起動が数秒で完了した理由
今回の再起動はほぼ即時で Ready になったが、これは条件が特別に良かったため。TiKV の再起動時間はほぼ「持っているデータ(region 数と Raft ログ)の回復量」で決まる。
GC 完了後の region はクラスタ全体で 5 個 / 約 24MB しかなく、RocksDB のオープンも Raft ステートの回復もほぼゼロ秒で終わる
Pod 削除といっても local-path の PV は同じノードに残るので、データ移動や再レプリケーションは一切発生しない。PD がストアを down 扱いにしてレプリカ補充を始めるのはデフォルト 30 分後(
max-store-down-time)なので、その前に戻ってくれば何も起きないブロックキャッシュは永続化されないため「積み直し」は起動後に読み取りが来たとき遅延的に行われるだけで、起動時間には影響しない
500 万行 / 33GB が入ったままなら、数千 region の Raft 回復とリーダーの退避・再配置が入るので Ready まで今回よりはっきり時間がかかる(それでも数十秒〜数分オーダー)。「TiKV は常にこんなに速い」ではなく「ほぼ空のストアの再起動はただのプロセス再起動と同じ」という理解が正しい。
B. ブロックキャッシュ容量の動的縮小(再起動不要・即時解放)
数少ない「実行するとメモリが実際に解放される」操作。TiDB に mysql 接続して実行する。
SET CONFIG tikv `storage.block-cache.capacity` = '2GB';
恒久化する場合は cluster/manifests/tidb-cluster/tidb-cluster.yaml の [storage.block-cache] capacity も合わせて変更して apply する(動的変更はプロセス再起動で失われる)。