前 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_WAITS、tikv_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 数达到数十万甚至百万级别,两处会先撑不住:
- TiKV 侧:
raftstore线程需要逐个处理每个 Region 的消息,Region 数越多,轮询和心跳开销越大;官方博客(A 级)指出这可能让单个 raftstore/gRPC 线程 CPU 打到瓶颈; - 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,形成持续的写热点。官方文档给出的两个缓解手段:
SHARD_ROW_ID_BITS:把隐式 RowID 打散到多个 bit 前缀,分散到不同 Region;AUTO_RANDOM:针对自增主键场景专门设计的打散方案。
判断写热点的信号:某个 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_client 的
wait duration
指标异常升高——这个指标衡量的是”等待 PD 返回 TSO
请求”的耗时。
4.2 常见根因
官方排障文档列出的典型原因:
- PD 节点磁盘 I/O 饱和:PD 与其它高 I/O 需求的组件混部,或磁盘本身存在问题;
- 网络问题:TiDB 到 PD Leader 的网络延迟升高或抖动;
- PD Leader 选举:切换 PD Leader 会带来官方文档标注的约 3 秒的性能抖动窗口,这是选举过程的正常代价,不是故障;
- PD Region 加载慢:与第二节的”Region 过多”根因重叠——PD Leader 切换后要重新从 etcd 加载全部 Region 元信息,加载慢会连带影响 PD 对外服务(包括 TSO)的恢复速度。
4.3 排查顺序
- 先看 Grafana PD 面板的磁盘延迟/负载指标,排除硬件问题;
- 看
blackbox_exporter的 ping 延迟,排除网络问题; - 检查是否恰好发生了 PD Leader 切换(维护操作或故障触发的选举);
- 若怀疑与 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"}:
- 若该指标只在特定 TiKV 实例升高,说明冲突集中在某几个热点 Region;
- 若该指标在所有实例普遍升高,说明冲突来自应用层的访问模式(例如大量并发更新同一批行),而不是某个 Region 的问题。
5.3 常见处置
- 高并发同 key 更新:开启
tidb_pessimistic_txn_fair_locking(新集群默认开启),代价是冲突场景下平均延迟可能上升,但公平性更好,避免个别事务被持续插队饿死; - Lock wait timeout:悲观事务等锁超过
innodb_lock_wait_timeout会返回错误而不是无限等待; - TTL manager timeout:悲观事务的锁 TTL
有上限(默认由
performance.max-txn-ttl控制,默认约 1 小时),超时未提交的长事务会被判定失败——这类超时往往是业务逻辑持有事务时间过长,而不是 TiKV 本身的问题。
这些机制的锁生命周期细节(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、加索引还是调整业务逻辑。
八、小结
三句话小结:
- 五类高频故障对应五条独立的信号链:Region 元信息与心跳(Region 过多)、CPU/QPS 分布(热点)、PD 时间戳等待(TSO 抖动)、Lock View(锁冲突)、raftstore/apply 线程指标(apply 积压)——排查时先分层定位,再进入具体机制。
- 很多”看起来是同一个症状”的问题根因链会在 PD Region 加载、写热点等环节交叉重叠,孤立看单一指标容易误判。
- 本文只给信号与机制;具体参数阈值随版本演进,请以当前版本官方 Troubleshooting Map 与 Grafana 面板定义为准。
参考资料
- TiDB Docs, TiDB Troubleshooting Map(docs.pingcap.com/tidb/stable/tidb-troubleshooting-map,A 级)。
- TiKV Blog / Best Practices, TiKV Performance Tuning with Massive Regions(tikv.org/blog、docs.pingcap.com/best-practices,A 级)。
- TiDB Docs, Troubleshoot Hotspot Issues(docs.pingcap.com/tidb/stable/troubleshoot-hot-spot-issues,A 级)。
- TiDB Docs, Troubleshoot Lock Conflicts、DATA_LOCK_WAITS(docs.pingcap.com/tidb/stable,A 级)。
- TiDB Docs, Troubleshoot High Disk I/O Usage、Troubleshoot Increased Read and Write Latency、Latency Breakdown(docs.pingcap.com/tidb,A 级)。
- 本系列 第 3–4 篇 Region/Multi-Raft、第 5 篇 raftstore 写路径、第 8 篇 Split/Merge/热点、第 9 篇 TSO、第 11 篇 悲观事务与 ResolveLock。
上一篇:CockroachDB 对照:range + Raft
下一篇:选型与阅读地图
返回 系列目录
同主题继续阅读
把当前热点继续串成多页阅读,而不是停在单篇消费。
【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 的负载分裂叙事。
【TiKV / HTAP 内核】Multi-Raft:一 Region 一 Raft 组
拆解 TiKV 的 Multi-Raft 架构:为什么每个 Region 是独立的 Raft 组,与 etcd 单 Raft 组对照差在哪;跨 Region 事务代价作为开放问题收束,不重写 Raft 协议本身。
【TiKV / HTAP 内核】raftstore 写路径:propose 到 Apply
拆解一次 TiKV 写请求经 propose、append、commit、apply 四个阶段落到 RocksDB 的路径,钉住 raft 线程与 apply 线程的异步边界;不编造未实测的 QPS 数字。
【TiKV / HTAP 内核】Snapshot、log GC 与副本迁移
拆解 TiKV 落后副本如何靠 Raft Snapshot 追赶进度、Raft 日志如何在 apply 完成后安全截断(log GC),并简述 witness/learner 副本形态;TiFlash Learner 的列存链路留给第 14 篇。