作者:黄俊深
TiFlash 是 TiDB HTAP 形态的关键组件 ,它是 TiKV 的列存扩展,在 提供了良好的隔离性的同时,也兼顾了强一致性 。列存副本通过 Raft Learner 协议异步复制,但是在读取的时候通过 Raft 校对索引配合 MVCC 的方式获得 Snapshot Isolation 的一致性隔离级别。这个架构很好地解决了 HTAP 场景的隔离性以及列存同步的问题。
使用 TiFlash 前,需要给表添加 TiFlash 副本。不少用户反馈添加 TiFlash 副本的时候出现问题。 TiFlash 副本始终处于不可用状态 官方文档总结了一些简单的问题排查。
这篇文章将介绍 目前版本 (目前所有 release 的 4.x, 5.x 版本) 下给 TiDB 中的表添加 TiFlash 副本的工作原理 ,主要供 DBA 同学们排查相关的问题时,可以从中参考先从哪些方面收集信息及尝试解决。
基本概念
在 PD 的视角里,TiFlash 实例与 TiKV 实例类似,都是一个 store,只是 TiFlash 的 store 会带有 “key=engine, value=TiFlash” 的一个 label。添加 TiFlash 副本后,PD 把 region 调度到 TiFlash,并让其中的 region 一直只以 learner 的形式存在,依赖的是 Placement Rules 功能 。
TiFlash 实例中包含有一个修改版本的 TiKV 代码,主要负责与 TiKV 协同处理 Raft 层的操作,其输出日志与 TiKV 基本一致。TiUP 部署时,其日志会输出到 tiflash_tikv.log。
TiFlash 实例会定期启动一个子进程来处理与 TiFlash 副本添加、删除相关的操作 。如果在进程列表中偶尔看到一个名为 tiflash_cluster_manager 的不常驻进程(在官网中称为 “pd buddy”),属于正常情况。其日志会输出到 tiflash_cluster_manager.log。
TiFlash 内部组件架构图
添加 TiFlash 副本各阶段集群中组件的工作
添加 TiFlash 副本的时序图
执行副本数修改 DDL
在 TiDB 中执行 alter tableset tiflash replica
时,这条语句作为 DDL 语句执行。
从 progress 0.0 到 1.0 的同步过程中
TiDB 提供 http 接口,其他组件可以通过此接口查询哪些表存在 TiFlash 副本: curl http://:/tiflash/replica
。
TiFlash 有 定期任务 ,负责:
- 从 TiDB 的 /tiflash/replica 接口拉取哪些表/分区有 TiFlash 副本。对于未 available 的表,如果表在 PD 上没有相应的 Placement Rules,该任务会负责设立相应的 rule,key range 为 [ t__r, t__ )。
- 对于未 available 的表,该任务会从 PD 拉取 key range 对应的 region_id,以及在线的所有 TiFlash store 中有多少已经同步的 region_id。
- 以 TiFlash store 中去重后的 region_id 个数 / PD 中 region_id 个数,通过给 /tiflash/replica 接口发 POST 请求的方式更新同步进度 progress。
- 如果 PD 中存在 placement-rules 但 /tiflash/replica 中不存在相应的 table_id,说明该表/分区已经被 DROP 而且已经过了 GC 时间,会到 PD 中移除相应的 rule。
该组件的日志输出为 tiflash_cluster_manager.log。如果集群中存在多个 TiFlash,会通过 PD 内置的 etcd 选出一个来负责上述任务。通过日志排查时需要拿到该时间段内负责该工作的节点,或者拿全部 TiFlash 节点的相关日志。
PD 的行为:接收到 placement-rules 后 ,PD 会:
- 先对 Region 进行切分,确保 Region 的边界不会跨越 表数据 及 索引(因为 TiFlash 只同步表数据部分)
- 对 Region 的 Leader 下发 AddLearner 到 TiFlash store 的调度
TiKV 的行为:
- TiKV 中 Region 的 Leader 接受并执行 PD 的 AddLearner 命令
- Region Leader 以 Snapshot 形式把 Region 数据发送到 TiFlash 的 Region peer
对于已经有 TiFlash 副本的分区表进行 Add partition 的过程
TiDB 对已经有 TiFlash 副本的分区表进行 Add partition 时,会在生成 partition 后(但对用户不可见)block 并等待。直到 TiFlash 上报该 partition 对应的 partition_id 已经 available 后,DDL 才执行完成。(详细内容可参考 TiDB 相关 PR )
对于 TiFlash 而言,给分区表添加一个 partition 与添加一个普通表是类似的操作,可以参考上文的流程。不同的是在此情况下,会额外在 PD 添加 accelerate-schedule 的操作,提升分区表 key range 相关 Region 的调度优先级, 以期望在集群繁忙的情况下,缩短分区表的 available 速度,减少 DDL block 的时间。
为什么需要 block 分区表的 Add partition 操作:
- 假如不 block Add partition 的 DDL 操作,在用户执行查询语句时(比如 count(*) ),如果查询选择了从 TiFlash 读,但是新 partition 上的 region 还没有建立起 TiFlash 副本,此时会导致用户的查询因为少数的 region 而失败。表现出来为用户在执行 Add partition 时,查询该表不稳定,容易失败。
- 为了避免造成查询的不稳定,block 分区表的 Add partition 操作,待新建分区的 Region 建立完 TiFlash 副本 ready 后才允许读到该分区。
不同阶段出现问题时排查的方向(举例)
执行 alter tableset tiflash replica
时卡住
通常来说,这句 DDL 操作仅修改 TiDB 中的元信息,执行时不会阻塞太久。如果出现执行此语句卡住的问题,可以看是否有其他 DDL 操作 block 了该语句的执行(比如在同一个表上是否存在 add index 操作)。更多地可以参考其他 TiDB 中 DDL 卡住的经验 [FAQ] DDL 卡住排查经验 - TiDB 常见 FAQ 。
副本数修改成功,但是 progress 一直为零,或者 progress 有进展,但是很 “慢”
- 先根据 TiFlash 副本始终处于不可用状态 确认下基本的问题
- 上述排查无误的情况下,先检查 tiflash_cluster_manager.log 的日志。看是否与 TiDB 或 PD 连接出现异常,如果有异常,先确认是相关组件的 API 查询超时(curl http://:10080/tiflash/replica,见 TiDB 与 TiFlash 同步接口)还是网络连通性有问题。
- 再确认出现问题的表是否有创建 placement-rule (tiflash_cluster_manager.log 日志中关键字 “Set placement rule … table–r”),上报给 TiDB 的进度信息(id, region_count, flash_region_count)。确认 PD 上是否能够查询到相应表的 rule (参考 Placement Rules 使用文档 )。
- 确认同步进度 “慢” 的具体表现。出问题的表,其 flash_region_count 是否很长时间”没有变化”,还是只是 “变化得慢” (比如几分钟还是会涨几个 region)。
- 如果是 “没有变化” ,需要排查整个工作链路上什么环节出现问题。 TiFlash 给 PD 设 rule -> PD 给 TiKV 中的 Region leader 下发 AddLearner 调度 -> TiKV 给 TiFlash 同步 Region 数据 这个链路是否有问题,收集相关组件的日志进行排查。 可以检查 tikv、tiflash-proxy 日志中的 warn/error 信息,确认是否存在网络隔离之类的错误。
- 如果是 “变化得慢” ,可以排查 TiFlash 当前的负载、PD 的调度。 主要观察 Grafana 中的 TiFlash-Summary 看板,Raft 中 “Applying snapshots Count”、“Snapshot Predecode Duration”、“Snapshot Flush Duration” 几个图,反映 TiFlash 通过 ApplySnapshot 接收数据的并发度、apply 耗费的时长;以及 Storage Write Stall 中的 “Write Stall Duration” 是否写入太频繁,导致出现了 Write Stall 现象;收集其他如CPU、磁盘IO负载等,以及 TiFlash 的日志。 PD 相关的调度参数调整见: PD 调度参数 。
对已经有 TiFlash 副本的分区表进行 Add partition 过程中卡住
根据 PR 中的 comment ,如果是因为 TiFlash 没有建立起副本而 block 住,会打印 “[ddl] partition replica check failed” 的日志。接下来的排查方向,大概是当时的是否有较多 Region 在建立 TiFlash 副本、TiFlash apply snapshot 的压力、PD 调度优先级是否有生效等。
附录:
一些过程中辅助排查的 API:
TiDB 中查询 TiFlash 副本、进度等
select * from information_schema.tiflash_replica
查看最近执行 / pending 的 DDL 任务
admin show ddl jobs
TiDB 中获取 TiFlash 副本消息的 API 接口(与 TiFlash 交互的主要接口)
curl http://:/tiflash/replica
TiDB 中查询表的 Region 信息
SHOW TABLEREGIONS
查询单个 TiFlash 节点上 table_id 对应的 Region 信息
echo "DBGInvoke dump_all_region(,true)" | curl "http://:/?query=" --data-binary @-
PD 中查询 Region 的信息
tiup ctl pd -u http://:region
PD 中查询 Placement-rules 信息
tiup ctl pd -u http://:config placement-rules show