问题排查
监控图如下所示,可以明显看到线上集群中有一个TiKV节点负载特别高,其他节点都是40%十分稳定,只有它会短时间出现80%的尖刺,然后会很快恢复。
这时候就可以通过 TiDB dashboard 的 TopSQL 来定位,到底是那些 SQL 占用了大量 CPU。通过交叉对比,可以发现只有这个store 7 的 TiKV 节点上,TopSQL里才有这几个 pre_asset 开头表的 REPLACE INTO操作。
看到上面的监控指标,直观印象是这些表肯定是存在热点写入的,一开始还以为是自增主键导致的,但是进一步排查表结构,发现其主键是PRIMARY KEY (`account3_id`,`txn_account_id`,`fund_code`)
,已经是按照账号维度来设计了,每个账号的数据量相差不大且分布比较均匀,理论上不该存在热点的。
这时候就怀疑是这些表的 region 分布是不均匀的,接下来通过 SQL 来查询最大表的 region 分布情况,发现虽然表在多个 TiKV 中虽然整体分布均匀,但是 90% 的 Leader 集中在 store 7上,难怪 store 7 的 CPU 这么高。
根因分析
已经定位到问题是 Leader 分布不均匀导致的,后续就需要分析一下这种情况出现的原因。经过和业务调研,这些表的使用场景是:这几个表都是资产预估相关的表,每天下午 15 点收市后会执行下 truncate table清空数据,在 19 点净值更新时候,每隔 10min 会有一次集中批处理写入,每次持续 5min 左右。
其实和官方文档里 热点问题产生的原因 这节类似,因为这几个表每天都是从1个 region 分裂成多个 region的,而在region 分裂后会向所有 store 发生一个Leader 选举的请求,正常都是初始 region 所在的 TiKV 节点响应是最快的,所以基本上都是那台 TiKV 抢到了新 region 的 Leader,也就导致这几个表有 90% 的 Leader 都在那台 TiKV 上。
这其实就是文档里说的"预热阶段"类似,本来如果这几个表有持续写入,TiDB 的hot region 迁移机制也能保证 Leader 按照负载均衡分布,但是这些表因为是批处理写入热点持续时间很短,在触发 hot region 机制之前已经结束了,也没有被迁移走。
解决方案
既然 CPU 负载不均衡,是因为 Leader 分布不均匀导致,那解决方案可以就此来处理,大概有以下几个思路。
1. 创建表时候就预分区
本方法只适用于非聚簇索引的表,对聚簇索引而言,只能通过下面两种方法来避免
TiDB 文档里有说明,通过开启系统变量 tidb_scatter_region,并在建表时指定 SHARD_ROW_ID_BITS + PRE_SPLIT_REGIONS 就能保证在建表时候就预分配好 region,这样在写入时候,就会直接写到对应分区,避免热点的出现。
注意: 由于 TiDB 默认会 merge 空的分区,所以还需要通过 pd-ctl 将 split-merge-interval 调大,防止这些表在真正有数据写入前又被 merge 成一个 region 了。
2. 写入前进行 SPLIT REGION
因为表每天数据分布都差不多,可以提前获取表某天表的 region 分布,通过 SPLIT REGION 在业务执行truncate table后,手动对其进行划分。
3. 写入后打散表的 REGION
这里还没办法直接通过 pd-ctl 来处理,需要手动调 tidb-server 的 http_api 来解决。
- 使用以下 API 打散特定表 region
curl http://{TiDBIP}:10080/tables/{db}/{table}/scatter
- 但是正如文档所言,这个功能和 pd 全局的调度可能会产生冲突,所以需要在确认已经打散后,手动再将其关闭
curl http://{TiDBIP}:10080/tables/{db}/{table}/stop-scatter
实战效果如下,经过打散后,本来是集中在 store 7 上的 Leader 被均匀分配到其他 TiKV 上了。