土法炼钢兴趣小组的算法知识备份

【TiKV / HTAP 内核】生产排障:Region 过多、热点、TSO 抖动、锁冲突、apply 积压

文章导航

分类入口
databasestorage
标签入口
#tikv#troubleshooting#hotspot#tso#lock-conflict#raftstore#apply-backlog#region-count

目录

前 16 篇把 TiKV/PD/TiFlash 的机制从下往上拆完了:Region 模型、Multi-Raft、写路径、PD 调度、TSO、Percolator 事务、Coprocessor 下推、TiFlash Learner。生产运维遇到的问题几乎都能映射回这些机制中的某一环,但反过来从”延迟变高了”这句话找到具体是哪一环,需要一张症状到机制的排查图。本文给出五类高频故障——Region 过多、热点、TSO 抖动、锁冲突、apply 积压——各自的信号源、根因链条与本系列对应章节的回链。

本文不重复调参手册;具体参数取值以官方文档当前版本为准,正文只给出信号与机制,不承诺固定阈值适用于所有集群。

版本锚定:以 TiDB/TiKV 7.x/8.x 官方文档为准;Grafana 面板名称、information_schema 系统表以对应版本文档为准,版本差异处请对照官方 TiDB Troubleshooting Map(A 级)。


一、排查框架:先分层再下钻

flowchart TD
  SYM["Symptom: latency spike / timeout / slow query"]
  SYM --> Q1{"Region count per store abnormal?<br/>raftstore/gRPC thread CPU high"}
  Q1 -->|yes| REGIONCNT["Massive regions<br/>see Section 2"]
  Q1 -->|no| Q2{"CPU/QPS concentrated<br/>on one store or one Region?"}
  Q2 -->|yes| HOTSPOT["Hotspot<br/>see Section 3"]
  Q2 -->|no| Q3{"pd_client tso wait duration high?"}
  Q3 -->|yes| TSOJIT["TSO jitter<br/>see Section 4"]
  Q3 -->|no| Q4{"DATA_LOCK_WAITS non-empty,<br/>scheduler latch wait high?"}
  Q4 -->|yes| LOCKCONF["Lock conflict<br/>see Section 5"]
  Q4 -->|no| Q5{"apply cpu / apply log duration high?"}
  Q5 -->|yes| APPLYBL["Apply backlog<br/>see Section 6"]
  Q5 -->|no| OTHER["Disk IO / network /<br/>schema change / GC — outside this article"]
层级 先问什么 首选信号
Region 拓扑 单节点 Region 数是否失控? Grafana TiKV-Details → Region count;PD region loading 耗时
负载分布 CPU/QPS 是否集中在少数节点或 Region? Hot Write/Thread_CPU 面板,Key Visualizer
时间戳服务 事务是否卡在等 PD 返回时间戳? pd_client_cmd_handle_cmds_duration_seconds{type="wait"}
并发控制 是否有事务在互相等锁? information_schema.DATA_LOCK_WAITStikv_scheduler_latch_wait_duration_seconds
写入执行 Raft propose/append/apply 哪一段慢? Raft Propose/Raft I/O/apply cpu 面板

二、Region 过多

2.1 机制

TiKV 把 key 空间按 第 3 篇讲过的 Region 模型切分,每个 Region 对应一个独立 Raft 组,需要独立的心跳、日志复制与状态维护(第 4 篇 Multi-Raft)。当单节点 Region 数达到数十万甚至百万级别,两处会先撑不住:

  1. TiKV 侧raftstore 线程需要逐个处理每个 Region 的消息,Region 数越多,轮询和心跳开销越大;官方博客(A 级)指出这可能让单个 raftstore/gRPC 线程 CPU 打到瓶颈;
  2. PD 侧:PD 需要把所有 Region 的元信息持久化在其内嵌的 etcd 上;Region 数越多,PD Leader 切换后重新从 etcd 加载全部 Region 元信息的耗时越长,官方文档给出的现象是日志里出现 load N regions cost XX s,数量级可以到十几秒甚至更长。

2.2 信号与处置方向

信号 面板/命令 处置方向
raftstore/gRPC 线程 CPU 接近饱和 Grafana Thread CPU(按 instance, name 展开) 增大 raftstore.store-pool-size(需确认 CPU 资源充足)
PD Region 加载慢 PD 日志 grep "regions cost" 开启 use-region-storage,减少 etcd 加载耗时
大量小/空 Region pd-ctl region 统计 Region 平均大小 开启 Region Merge,合并相邻小 Region
空闲 Region 心跳浪费 Region 数高但写入极少 开启 Hibernate Region,减少空闲 Region 的心跳消息

Region Merge 与 Split 是一对相反操作(第 8 篇已讲机制),排障时的关键判断是:这些 Region 是”数据量大导致的正常 split”还是”删表/truncate 后留下的空 Region”——后者是 Region Merge 最典型的收益场景。


三、热点:写热点与读热点

3.1 写热点

TiDB 默认用连续递增的 RowID/AutoIncrement 主键编码行 key,这类单调递增的 key 会持续追加到 key 空间的末端,落在同一个 Region 里——即使这个 Region 因为体积触发了 split,新写入依然只会落到 split 后仍处于末端的那个 Region,形成持续的写热点。官方文档给出的两个缓解手段:

判断写热点的信号:某个 TiKV 节点的 Raftstore CPU 明显高于其他节点Hot Write 面板)。

3.2 读热点

读热点的信号是某节点的 Coprocessor CPU 明显偏高Thread_CPU 面板),常见于访问模式集中在少量 key 范围(例如某个大客户的数据全部落在几个 Region)。TiDB v7.1 引入的 load-based replica read 允许在 Leader 排队时间超过阈值(tidb_load_based_replica_read_threshold)时,把读请求转发到 Follower 副本执行,官方文档给出的收益范围是读热点场景下吞吐提升 70%–200%(A 级,标注为官方文档给出的场景化数字,非本站实测)。

flowchart LR
  WRITE["Write hotspot"] --> CAUSE1["Monotonic RowID<br/>always appends to tail Region"]
  CAUSE1 --> FIX1["SHARD_ROW_ID_BITS / AUTO_RANDOM"]
  READ["Read hotspot"] --> CAUSE2["Access pattern concentrated<br/>on few key ranges"]
  CAUSE2 --> FIX2["Load-based replica read<br/>route to follower when leader queues"]

3.3 与 PD 调度的边界

Region 分裂与 Leader 转移可以缓解热点,但响应速度有限——PD 的调度决策基于周期性 heartbeat 统计,不是实时感知;如果写入模式本身就是”持续追加到单一 key 前缀”,任何调度都只能延缓而不能根治,业务侧的 key 设计才是根本修复点(第 8 篇有更完整的调度机制拆解)。


四、TSO 抖动

4.1 机制

每次事务开始与提交都要向 PD 请求时间戳(第 9 篇)。TSO 抖动的直接信号是 pd_clientwait duration 指标异常升高——这个指标衡量的是”等待 PD 返回 TSO 请求”的耗时。

4.2 常见根因

官方排障文档列出的典型原因:

4.3 排查顺序

  1. 先看 Grafana PD 面板的磁盘延迟/负载指标,排除硬件问题;
  2. blackbox_exporter 的 ping 延迟,排除网络问题;
  3. 检查是否恰好发生了 PD Leader 切换(维护操作或故障触发的选举);
  4. 若怀疑与 Region 数量相关,回到第二节的处置方向。

五、锁冲突

5.1 信号:Lock View

TiDB 自 v5.1 起提供 Lock View(information_schema.DATA_LOCK_WAITS),直接列出当前正在等锁的事务 start_ts、持锁事务的 start_ts、被阻塞的 SQL digest 与具体 key。悲观事务的等锁信息可以直接从这张系统表拿到;乐观事务被阻塞时,SQL_DIGEST 字段为空,需要联表 CLUSTER_TIDB_TRX 才能定位具体语句。

5.2 判断冲突来源

排查写延迟时,官方文档给出的关键指标是 tikv_scheduler_latch_wait_duration_seconds{type="acquire_pessimistic_lock"}

5.3 常见处置

这些机制的锁生命周期细节(TTL、ResolveLock)已在第 11 篇拆过,本节只给排查入口。


六、apply 积压

6.1 机制回顾

写路径(第 5 篇)分 propose → append → commit → apply 四段,apply 是把已提交的日志实际写入 RocksDB 的那一步。apply 积压意味着日志已经共识通过,但还没来得及落进本地存储引擎,表现为写延迟升高但共识本身没有问题。

6.2 信号与常见根因

官方排障文档给出的信号组合:

信号 含义 常见根因
apply cpu 高 且 apply log duration apply 线程处理不过来 apply-pool-size 过小(建议区间 [1, 5]);CPU 资源不足
单个 apply 线程 CPU 显著高于其它线程 写热点落在少数 Region 见第三节热点处置
RocksDB kv/max write duration 写入 RocksDB 本身慢 磁盘 I/O 瓶颈;一条 Raft 日志可能批量包含多个 kv(例如 128 个一批),单批写入放大了单次 RocksDB 写延迟
Raft Propose/propose wait duration 高但 append log duration 不高 提议排队,而非磁盘慢 store-pool-size 过小
commit log duration 高伴随写热点 单 Region 的 Raft 复制窗口受限 适当调大 raft-max-inflight-msgs(类似 TCP 滑动窗口)

6.3 决策树

flowchart TD
  SLOW["Write latency high"] --> C1{"propose wait high,<br/>append log normal?"}
  C1 -->|yes| POOL["store-pool-size too small<br/>or CPU insufficient"]
  C1 -->|no| C2{"apply cpu / apply log duration high?"}
  C2 -->|yes| C3{"single apply thread<br/>much hotter than others?"}
  C3 -->|yes| HOT2["Write hotspot on one Region<br/>see Section 3"]
  C3 -->|no| POOL2["apply-pool-size too small<br/>or CPU insufficient"]
  C2 -->|no| C4{"RocksDB write duration high?"}
  C4 -->|yes| DISK["Disk IO bottleneck<br/>outside raftstore layer"]
  C4 -->|no| C5{"commit log duration high?"}
  C5 -->|yes| WINDOW["raft-max-inflight-msgs<br/>too small for hot Region"]

七、常见误解

误解一:Region 数越少越好,应该尽量关闭自动 split。 Region 太少同样有问题——单个 Region 承载过多数据或过高的写入压力,会让该 Region 的 Raft 组本身变成瓶颈(对照第三节热点问题);Region 数量的目标是”与集群规模和负载匹配”,不是越少越好,也不是越多越好。

误解二:出现锁冲突就是业务代码写得不好。 不一定。有些锁冲突来自执行计划意外触发了大范围扫描并加锁(例如缺索引导致的全表扫描式更新),或者是热点 Region 上的正常并发竞争;先用 Lock View 和 scheduler_latch_wait 指标定位是”少数 Region 热点”还是”应用层普遍冲突”,再决定改 SQL、加索引还是调整业务逻辑。


八、小结

三句话小结:

  1. 五类高频故障对应五条独立的信号链:Region 元信息与心跳(Region 过多)、CPU/QPS 分布(热点)、PD 时间戳等待(TSO 抖动)、Lock View(锁冲突)、raftstore/apply 线程指标(apply 积压)——排查时先分层定位,再进入具体机制。
  2. 很多”看起来是同一个症状”的问题根因链会在 PD Region 加载、写热点等环节交叉重叠,孤立看单一指标容易误判。
  3. 本文只给信号与机制;具体参数阈值随版本演进,请以当前版本官方 Troubleshooting Map 与 Grafana 面板定义为准。

参考资料

  1. TiDB Docs, TiDB Troubleshooting Map(docs.pingcap.com/tidb/stable/tidb-troubleshooting-map,A 级)。
  2. TiKV Blog / Best Practices, TiKV Performance Tuning with Massive Regions(tikv.org/blog、docs.pingcap.com/best-practices,A 级)。
  3. TiDB Docs, Troubleshoot Hotspot Issues(docs.pingcap.com/tidb/stable/troubleshoot-hot-spot-issues,A 级)。
  4. TiDB Docs, Troubleshoot Lock ConflictsDATA_LOCK_WAITS(docs.pingcap.com/tidb/stable,A 级)。
  5. TiDB Docs, Troubleshoot High Disk I/O UsageTroubleshoot Increased Read and Write LatencyLatency Breakdown(docs.pingcap.com/tidb,A 级)。
  6. 本系列 第 3–4 篇 Region/Multi-Raft第 5 篇 raftstore 写路径第 8 篇 Split/Merge/热点第 9 篇 TSO第 11 篇 悲观事务与 ResolveLock

上一篇CockroachDB 对照:range + Raft

下一篇选型与阅读地图

返回 系列目录

同主题继续阅读

把当前热点继续串成多页阅读,而不是停在单篇消费。

2026-07-16 · database / storage

【TiKV / HTAP 内核】Split / Merge / 热点:Region 何时该切、何时该并

拆解 TiKV Region 何时触发 size/key-count 分裂、如何用 BatchSplit 在不拷贝数据的前提下切分 RocksDB key range;PrepareMerge/CommitMerge 两阶段合并;Load Base Split 与 PD hot-region-scheduler 两层热点机制的分工,并对齐 distributed/27 的负载分裂叙事。

2026-07-16 · database / storage

【TiKV / HTAP 内核】Snapshot、log GC 与副本迁移

拆解 TiKV 落后副本如何靠 Raft Snapshot 追赶进度、Raft 日志如何在 apply 完成后安全截断(log GC),并简述 witness/learner 副本形态;TiFlash Learner 的列存链路留给第 14 篇。


By .