【SOP 系列 25】GC 常见问题排查

GC 机制

详见官网文档 GC 机制简介

诊断工具

1. 重要监控指标

GC Tasks & GC Tasks duration:gc_worker 处理的 GC 任务的个数和执行 GC 任务时所花费的时间。

  • total-gc: 总的 GC 任务数。每个 GC 任务会清理一个 region 待 GC 的 keys(compaction filter 关闭时有效)。

  • skipped-gc/failed-gc: 跳过的 GC 任务和失败的 GC 任务(compaction filter 关闭时有效)。GC 时根据 rocksdb 记录的元信息判断,当没有很多版本要清理的 region 可以跳过。

  • total-unsfe_destroy_range: Delete Ranges 阶段调用的 UnsafeDestroyRange 任务数。

  • total-orphan_versions / total-gc_keys: compaction filter 发起的 GC 任务,用于处理 compaction filter 不能直接清理的数据。

Auto GC Progress:各个 TiKV Do GC 阶段的进度(compaction filter 关闭时有效)。

  • 每一轮 GC TiKV scan 数据时进度会从 0 涨到 100%,这个指标只是粗略计算,可能比 TiDB 的 GC 周期要慢一些,如果 TiKV GC 不活跃则为进度 0。

Auto GC SafePoint:各个 TiKV Do GC 阶段使用的 safepoint,该 safepoint 由每台 TiKV 定期从 PD 获取。

GC Speed:GC 时每秒删除的 key 的数量。无论是否开启 compaction filter 都有效。

GC scan write details / GC scan default details:GC scan 时 RocksDB iterators 统计的操作事件,分为 write CF 和 default CF,与 Coprocessor - Total Ops Details by CF 指标类似。

GC in Compaction Filter:write CF 的 compaction filter 中已过滤版本的数量。

  • filtered: compaction 时过滤掉的所有 keys 数量,类型包括 put、delete、rollback、lock 等 。
  • rollback/lock: compaction 时过滤的 keys 当中包含的 rollback 和 lock keys 的数量,rollback/lock keys 只是在事务回滚和 select for update 时产生的特殊 key,不代表实际数据。

2. 系统表 mysql.tidb

以 tikv_gc 开头的变量都与 GC 相关,其中 tikv_gc_leader_uuid/tikv_gc_leader_desc/tikv_gc_leader_lease 用于记录 GC leader 的状态,tikv_gc_safe_point 和 tikv_gc_last_run_time 在每轮 GC 开始前会被自动更新,其他几个变量则是可配置的,详见 GC 配置

注意 :从 5.0 开始建议通过 set 变量的方式来设置 tidb_gc_life_time 等参数,而不是直接修改 update mysql.tidb,避免修改成错误格式的参数导致 GC 不正常。

3. GC leader 日志

通过 mysql.tidb 表的 tikv_gc_leader_desc 字段找到 leader 所在的 TiDB 节点,然后过滤 gc_worker 关键字的日志。

grep "gc_worker" tidb.log | less

4. CLI 命令

pd-ctl service-gc-safepoint 查询所有 service 的 gc safepoint。

  • “service_gc_safe_points”: gc_worker/ticdc/br/dumpling 等各 service 记录的 safe point。
  • “gc_safe_point”: 用于通知 TiKV 进行 Do GC 的 safe point,该 safe point 不保证在此时间之后的数据可以安全读取。

image

如何判断 GC 所在阶段

GC 分为 Resolve Locks, Delete Ranges 和 Do GC 三个阶段。

Resolve Locks 阶段:在 TiKV 一侧会产生大量的 scan_lock 请求,可以在 gRPC 相关的 metrics 中观察到。scan_lock 请求会对全部的 Region 调用。

Delete Ranges 阶段:会往 TiKV 发送少量的 unsafe_destroy_range 请求,也可能没有。可以在 gRPC 相关的 metrics 中和 GC - GC tasks 中观察到。

Do GC 阶段:每个 TiKV 自行检测 PD 记录的 safe point 是否更新,如果更新会执行 GC 操作(不同版本行为有差异),与此同时 GC leader 可以继续触发下一轮 GC,因此该阶段的执行过程和其他阶段是异步的,可以通过 TiKV - Details 页面的监控指标判断是否在该阶段:

  • 关闭 compaction filter 时,通过 GC - GC Tasks 中的 total-gc 判断
  • 开启 compaction filter 时,通过 GC - GC in Compaction Filter 判断
  • 无论是否开启 compaction filter,都可以通过 GC - GC Speed 判断

常见 GC 问题

1. 主要现象

  • safe point 长时间不推进
  • Drop table 后磁盘空间一直没有回收

2. 可能原因

  • gc_life_time 等变量格式错误
  • 未提交的长事务 block GC
  • 某些 service 的 safe_point block GC
  • Resolve Locks 失败
  • 将 GC life time 从一个较小的值调大
  • GC 运行很慢但正常
  • gc_life_time 或 gc_run_interval 等变量设置过大

3. 问题排查

检查 GC leader 日志是否有相关报错,判断可能的原因,以下面几种情况的报错为例:

  • Case1:报错 Failed to parse duration "time: unknown unit “min"in duration"10min”“

参数格式错误,误将 gc_life_time 改成了 10min,应改成 10m

  • Case2:报错 gc safepoint blocked by a running session

未提交事务 block GC,通过 show processlist 或 information_schema.cluster_processlist 找到 block session。

  • Case3:报错 there’s another service in the cluster requires an earlier safe point

某些 service 的 safe_point block GC,通过 pd-ctl service-gc-safepoint 找到 block service。

  • Case4:报错 resolve locks failed

通常是 Region unavailable 引起,通过 grep -E “gc_worker range_task” tidb.log 查询相关报错日志,排查 Region unavailable 原因。

  • Case5:日志出现 last safe point is later than current one. No need to gc

调大 GC life time 后,新一轮 GC 时 safe point 被重新计算,得到一个比上次 GC 的 safe point 更早的时间,因而无需进行 GC,这种情况不需要处理。

GC 对性能的影响

如果监控观察到 Duration 出现周期性抖动,并且与 GC 运行周期保持一致,可以判断 GC 对性能产生了影响。

5.0 版本默认开启 compaction filter 后,GC 对性能的影响明显下降,当出现类似问题,先检查 compaction filter 是否开启。对于 4.x 版本,可以尝试设置 GC 流控减少对性能的影响。

注意:v5.1.3/v5.2.3/v5.3.0 版本修复了 compaction filter GC 工作机制和 batch client 层的多个 Bug ,建议升级到较新的 release 版本后再开启 compaction filter。

注意事项

当遇到需要恢复误删除数据或调查数据损坏问题(如数据索引不一致)等情况时,可能要临时调大 gc life time 避免历史数据被清理,例如

  • update mysql.tidb set variable_value = "1000h" where variable_name = "tikv_gc_life_time"

  • set @@global.tidb_gc_life_time = "1000h"

处理完问题后,不要忘记将变量修改回之前的值,避免留存过多的历史数据对业务查询性能造成影响。

5赞