基本概念
在 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。
PD 的行为:接收到 placement-rules 后,PD 会:
对 Region 的 Leader 下发 AddLearner 到 TiFlash store 的调度
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