本文详细介绍了 TiDB 的 Garbage Collection(GC)机制及其在 TiDB 组件中的实现原理和常见问题排查方法。TiDB 底层使用单机存储引擎 RocksDB,并通过 MVCC 机制,基于 RocksDB 实现了分布式存储引擎 TiKV,以支持高可用分布式事务。GC 过程旨在清理旧数据,减少其对性能的影响,主要包括四个步骤:计算 GC safepoint、解析锁(Resolve locks)、连续范围数据删除(Delete ranges)和同步 GC safepoint 至集群其他组件。文章还讲述了如何定位 GC leader、监控 GC 状态、以及处理 GC 过程中遇到的常见问题。

如何查找 GC leader 在哪个 tidb 上?
mysql> select variable_name,variable_value from mysql.tidb where variable_name = "tikv_gc_leader_desc"\G;
*************************** 1. row ***************************
variable_name: tikv_gc_leader_desc
variable_value: host:172-16-120-219, pid:3628952, start at 2024-01-17 16:34:58.022047311 +0800 CST m=+9.910349289
1 row in set (0.00 sec)
找到 gc leader 对应的 tidb 实例,在日志中 grep "gc_worker" 关键字即可看到当前 GC 的整体运行状态。关于日志中字段的具体含义,我们可以在后面的章节会详细展开介绍。
tidb@172-16-120-219:~/shirly/tiup$ ps aux | grep 3628952
tidb 3592616 0.0 0.0 8160 2456 pts/2 S+ 15:12 0:00 grep --color=auto 3628952
tidb 3628952 8.2 0.2 10914336 1065168 ? Ssl Jan17 4158:25 bin/tidb-server -P 4005 --status=10080 --host=0.0.0.0 --advertise-address=127.0.0.1 --store=tikv --initialize-insecure --path=127.0.0.1:2379,127.0.0.1:2381,127.0.0.1:2383 --log-slow-query=/DATA/disk4/shirly/tiup/tidb-deploy/tidb-4005/log/tidb_slow_query.log --config=conf/tidb.toml --log-file=/DATA/disk4/shirly/tiup/tidb-deploy/tidb-4005/log/tidb.log
tidb@172-16-120-219:~/shirly/tiup$ grep "gc_worker" /DATA/disk4/shirly/tiup/tidb-deploy/tidb-4005/log/tidb.log | head
[2024/02/02 19:29:59.062 +08:00] [INFO] [gc_worker.go:1073] ["[gc worker] start resolve locks"] [uuid=6345a3cad480026] [safePoint=0] [try-resolve-locks-ts=447446541678411803] [concurrency=3]
[2024/02/02 19:29:59.291 +08:00] [INFO] [gc_worker.go:1095] ["[gc worker] finish resolve locks"] [uuid=6345a3cad480026] [safePoint=0] [try-resolve-locks-ts=447446541678411803] [regions=1236]
[2024/02/02 19:30:59.065 +08:00] [INFO] [gc_worker.go:1073] ["[gc worker] start resolve locks"] [uuid=6345a3cad480026] [safePoint=0] [try-resolve-locks-ts=447446557407051824] [concurrency=3]
[2024/02/02 19:30:59.283 +08:00] [INFO] [gc_worker.go:1095] ["[gc worker] finish resolve locks"] [uuid=6345a3cad480026] [safePoint=0] [try-resolve-locks-ts=447446557407051824] [regions=1236]
[2024/02/02 19:31:59.060 +08:00] [INFO] [gc_worker.go:1073] ["[gc worker] start resolve locks"] [uuid=6345a3cad480026] [safePoint=0] [try-resolve-locks-ts=447446573135691804] [concurrency=3]
[2024/02/02 19:31:59.297 +08:00] [INFO] [gc_worker.go:1095] ["[gc worker] finish resolve locks"] [uuid=6345a3cad480026] [safePoint=0] [try-resolve-locks-ts=447446573135691804] [regions=1236]
[2024/02/02 19:32:59.072 +08:00] [INFO] [gc_worker.go:347] ["[gc worker] starts the whole job"] [uuid=6345a3cad480026] [safePoint=447446510221131776] [concurrency=3]
[2024/02/02 19:32:59.074 +08:00] [INFO] [gc_worker.go:1073] ["[gc worker] start resolve locks"] [uuid=6345a3cad480026] [safePoint=447446510221131776] [try-resolve-locks-ts=447446588864331831] [concurrency=3]
[2024/02/02 19:32:59.305 +08:00] [INFO] [gc_worker.go:1095] ["[gc worker] finish resolve locks"] [uuid=6345a3cad480026] [safePoint=447446510221131776] [try-resolve-locks-ts=447446588864331831] [regions=1236]
[2024/02/02 19:33:59.057 +08:00] [INFO] [gc_worker.go:307] ["[gc worker] there's already a gc job running, skipped"] ["leaderTick on"=6345a3cad480026]
TiDB GC 整体流程主要分四个步骤,本章我们将逐一展开介绍。
计算 GC safepoint, 即 TiDB GC 时需要知道具体删除哪个时间点之前的旧版本。 清理 GC safepoint 之前事务留下的锁,即旧版本数据在被清理前,需要明确残留锁所在事务的状态。 Delete-ranges 连续范围数据删除,即对于 truncate table 等这类在 TiKV 中连续保存的数据,直接在此阶段进行物理删除以优化性能。 将最新 safepoint 同步到集群其他组件(TiKV)

Safepoint 是一个时间戳,对应一个具体的物理时间。 TiDB GC 会保证 ts >safepoint 的所有快照数据的安全性。

如果 gc safepoint 是 5:00, 则 GC 后只会保留 key_4:00 这条数据。 如果 gc safepoint 是 2:30, 则 GC 后会保留 key_4:00,key_3:00,key_02:00 这三条数据。以确保读 2:30 这一时刻的快照时,能读到 key_02:00 这条数据。

GC safepoint 的计算过程及常见问题
GC safepoint = Current time - GC lifetime (10min by default)
常见问题
tidb_172.26.55.107_4000.log:[2024/01/16 09:06:52.689 +08:00] [Info] [gc_worker.go:1613] ["[gc worker] sent safe point to PD"] [uuid=6342b1728dc000d] ["safe point"=447035326078648378]
tidb_172.26.55.107_4000.log:[2024/01/16 09:15:11.099 +08:00] [Info] [gc_worker.go:731] ["[gc worker] there's another service in the cluster requires an earlier safe point. gc will continue with the earlier one"] [uuid=6342b1728dc000d] [ourSafePoint=447035555467755520] [minSafePoint=447035326078648378]GC safepoint = min(GC safepoint,min_start_ts among all sessions)常见问题
[gc_worker.go:359] ["[gc worker] gc safepoint blocked by a running session"] [uuid=609099af5940005] [globalMinStartTS=437144990507073574] [safePoint=2022/11/04 23:29:59.969 +08:00]select * from INFORMATION_SCHEMA.CLUSTER_PROCESSLIST where TIMESTAMPDIFF(minute, now(), concat("2021-", substring_index(txnstart, "(", 1) )) < -10;GC safepoint = min(GC safepoint, min_service_gc_safe_points)常见问题
[2022/02/24 17:18:01.444 +05:30] [INFO] [gc_worker.go:411] ["[gc worker] gc safepoint blocked by a running session"] [uuid=5f56cf29bac008e] [globalMinStartTS=431397745740742701] [safePoint=431398894023213056]
[2022/02/24 17:18:01.450 +05:30] [INFO] [gc_worker.go:581] ["[gc worker] there's another service in the cluster requires an earlier safe point. gc will continue with the earlier one"] [uuid=5f56cf29bac008e] [ourSafePoint=431397745740742701] [minSafePoint=431337131664474119]
在确认了是这一步卡住的后,就可以通过 PD 查看一下具体是哪个服务卡住了 GC 并介入处理了。
// 通过 pd-ctl 查看 service_gc_safe_points 下面的服务需要的最旧快照对应的 safepoint
tidb@172-16-120-219:~/shirly/tiup$ tiup ctl:v7.5.0 pd -u http://127.0.0.1:2379 service-gc-safepoint
Starting component `ctl`: /home/tidb/.tiup/components/ctl/v7.5.0/ctl pd -u http://127.0.0.1:2379 service-gc-safepoint
{
"service_gc_safe_points": [
{
"service_id": "gc_worker",
"expired_at": 9223372036854775807,
"safe_point": 447873652897873920 // 这个是在当前这一步上传的 gc safepoint.
},
{
"service_id": "ticdc-default-157...",
"expired_at": 9223372036854645313,
"safe_point": 447873653919043430
}
],
"gc_safe_point": 447873652897873920 // 这个是 TiDB GC 最后一步上传的 safepoint, 用于通知 tikv 用。
}
// 计算每个 service safepoint 实际对应的时间。
tidb@172-16-120-219:~/shirly/tiup$ tiup ctl:v7.5.0 pd -u http://127.0.0.1:2379 tso 447873652897873920
Starting component `ctl`: /home/tidb/.tiup/components/ctl/v7.5.0/ctl pd -u http://127.0.0.1:2379 tso 447873652897873920
system: 2024-02-21 15:59:59.055 +0800 CST
logic: 0
tidb@172-16-120-219:~/shirly/tiup$ tiup ctl:v7.5.0 pd -u http://127.0.0.1:2379 tso 447873653919043430
Starting component `ctl`: /home/tidb/.tiup/components/ctl/v7.5.0/ctl pd -u http://127.0.0.1:2379 tso 447873653919043430
system: 2024-02-21 16:00:02.95 +0800 CST
logic: 118630Step 2: Resolve locks

事务 1:
事务 ID 即 start_ts 是 t1, commit_ts 为 t2。 更新 A,B,C 三个 key, 客户端 commit 且返回成功。 当前分布式事务 primary_key 是 A, 也就是事务的状态存在 A 上 。 B,C 上有当前 t1 所在事务的残留 lock。
事务 2:
事务 ID 即 start_ts 为 t3, commit_ts 为 t4。(t1<t2<t3<t4)。 更新 A, D 两个 key, 客户端 commit 且返回成功。 A,D 新版本写入成功,无残留锁。

将请求发给每个 region 的 leader 获取到 lock 根据 lock 状态,逐个 resolve lock:
向 PD 定位当前 lock 里面 primary-key 所在的 region 信息 向对应的 TiKV 发送获取当前 (primary-key,事务 ID ) 对应的事务状态 根据 (primary-key,事务 ID) 对应的状态:
事务已提交,向 tikv 提交 lock。
事务已 rollback, 向 tikv 发送回滚该 lock 的消息。
tidb_gc_concurrency(https://docs.pingcap.com/tidb/v6.5/system-variables#tidb_gc_concurrency-new-in-v50)Resolve locks 步骤中的线程数,默认 -1 表示使用 TiKV 实例的个数作为并发数。一般情况下不用去调整这个值。

[gc_worker.go:621]["[gc worker] resolve locks returns an error"]..不 hibernate 但 GC。一个 tikv 维护 1w 个 region 需要消耗 15% cpu 的开销。 不 hibernate 不 GC。一个 tikv 维护 1w 个 region 需要消耗 10% cpu 的开销。 hibernate 不 GC。一个 tikv 维护 1w 个 region 需要消耗 1% cpu 的开销。 hibernate 定期 GC。一个 tikv 维护 1w 个 region 需要消耗 1% cpu 的开销外加 GC 期间达到 10-15% 的开销。
Drop table/index Truncate table Drop partition ...



UpdateServiceGCSafepoint:在计算 GC safepoint 那一步调用,这步调用成功,意味着 TiDB 侧 GC 正式开始 UpdateGCSafepoint 在最后这一步“save gc safepoint to PD”执行,这一步调用成功,意味着TiDB侧 GC 正式结束。
在工具卡住 gc safepoint 之后,两接口的 gc safepoint 出现不一致。如果此时又正好调大了 gc lifetime, 那么在下一次 gc 成功执行完成之前,这两个接口对应的值会一直不一致。
「喜欢这篇文章,您的关注和赞赏是给作者最好的鼓励」
关注作者
【版权声明】本文为墨天轮用户原创内容,转载时必须标注文章的来源(墨天轮),文章链接,文章作者等基本信息,否则作者和墨天轮有权追究责任。如果您发现墨天轮中有涉嫌抄袭或者侵权的内容,欢迎发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。




