GC和compact的几个问题

【 TiDB 使用环境】生产环境 /测试/ Poc
生产环境

【 TiDB 版本】
v5.1.2

【复现路径】做过哪些操作出现的问题
【遇到的问题:问题现象及影响】
【 问题 1 】
key 在 gc 之后,但是还没 compcat 之前,sql 查找时还需要扫描这些 keys 吗?还是直接跳过这些 keys ?如果跳过大概是如何做到的或者是什么原理?

【 问题 2 】
可以通过如下接口查找行的多版本数据,但是这个数据无法区分是gc后的还是未被gc的,因为只有gc+compcat之后,通过这个接口查出来的多版本数据才会彻底消失
curl http://192.168.1.1:10080/mvcc/key/dbname/table_name/343930187735040

是否有接口可以查找多版本数据是否已经被 gc 了?

【 问题 3 】
bug-11217: 多key GC调用导致GC不工作,大量历史版本残留

关于这个问题,我们采取的方法是 设置 gc.enable-compaction-filter: false 以关闭 TiKV 的 compaction filter GC,使用旧的 GC 模式进行多版本 GC
但是效果不太好,下面是一个具体例子
下面的sql是在11月29号执行的,使用覆盖索引查找100行数据,其中 create_time<=1669267277441 转化为时间之后是 create_time<=‘2022-11-24 13:21:17’
但是我们在11月23号就关闭了gc compaction filter 使用老的gc方式
desc analyze SELECT id FROM table_xxx FORCE INDEX(idx_essyncstate_bizproduct) WHERE (biz_id=300 AND es_sync_state=1 AND product_id=300 AND create_time<=1669267277441) ORDER BY create_time DESC LIMIT 100\G
*************************** 4. row ***************************
id: └─Limit_22
estRows: 4.12
actRows: 189
task: cop[tikv]
access object:
execution info: tikv_task:{proc max:485ms, min:70ms, p80:412ms, p95:456ms, iters:46, tasks:43}, scan_detail: {total_process_keys: 189, total_keys: 34711190, rocksdb: {delete_skipped_count: 610, key_skipped_count: 34712398, block: {cache_hit_count: 29983, read_count: 1866, read_byte: 38.9 MB}}}
operator info: offset:0, count:100
memory: N/A
disk: N/A

【 问题 4 】
show config where type=‘tikv’ and name like ‘%enable-compaction-filter%’;
set config tikv gc.enable-compaction-filter=false;
在 5.1.2 版本里面把这个参数关掉,是不是可以完全的解决 gc 的 bug ?会不会有其他的风险?(如果线上大量集群开启老的 gc 方式)
官方建议不要在线修改参数,那关闭新的gc方式建议使用tikv-ctl方式?
https://docs.pingcap.com/zh/tidb/v5.1/dynamic-config

【 问题 5 】
执行计划中的 delete_skipped_count 和 key_skipped_count 是什么意思?
这两个字段的解释在多个地方有多个不同的解释,但是还是不明白什么意思,可以帮忙解释一下吗,便于分析问题
版本 1
delete_skipped_count: 表示该 key 已经 gc ,状态为 tombstone ,还没有 compaction
key_skipped_count : 表示同一个 key 的 mvcc 版本比较多,没有过 GC 时间
版本 2
Rocksdb_delete_skipped_count:RocksDB 读数据过程中已删除 Key 的扫描数。
Rocksdb_key_skipped_count:RocksDB 扫数据时遇到的已删除 (tombstone) Key 数量。
版本3
在 asktug 上的帖子里

下面是一个测试
create table t123 (id int primary key,name varchar(100),age int,city varchar(100));
alter table t123 add index idx_name(name);
insert into t123 values (1,‘user1’,1,‘bj’);
insert into t123 values (2,‘user2’,2,‘bj’);
insert into t123 values (3,‘user3’,3,‘bj’);
insert into t123 values (4,‘user4’,4,‘bj’);
insert into t123 values (5,‘user5’,5,‘bj’);
insert into t123 values (6,‘user6’,6,‘bj’);
insert into t123 values (7,‘user7’,7,‘bj’);
insert into t123 values (8,‘user8’,8,‘bj’);
insert into t123 values (9,‘user9’,9,‘bj’);
insert into t123 values (10,‘user10’,10,‘bj’);

desc analyze select name from t123 where name=‘user1’\G

表中存在 1 行 name=‘user1’ 数据 ,没做过任何更新,删除操作,部分执行计划如下
scan_detail: {total_process_keys: 1, total_keys: 2, rocksdb: {delete_skipped_count: 1, key_skipped_count: 2

表中存在 3 行 name=‘user1’ 数据 ,没做过任何更新,删除操作,部分执行计划如下
scan_detail: {total_process_keys: 3, total_keys: 4, rocksdb: {delete_skipped_count: 3, key_skipped_count: 6

感谢各位大佬

【资源配置】
【附件:截图/日志/监控】

要看 Key 的删除是怎么操作的,如果是 delete 操作,那么没有 compaction 之前的 key 还是会扫到,除非是 drop/truncate table 会不用等 compaction。

这个是查询当前的版本情况,已经被 gc 就不显示在这里了

建议升级版本,最新版本的分支已经解决该问题

关闭 compaction-filter 相当于回到开始的 gc 模式,实际影响就是 gc 清理效率方面的问题。建议升级版本到 compaction-filter 稳定版本

参考版本 2 的介绍,是 RocksDB 里面的逻辑,执行计划里面看到可以预估对实际查询的影响。

谢谢答复

问题1
这篇文章里的第二章tombstone key 引发的“血案”里说:gc后在没有被compact之前做了优化,是不需要扫描的

问题2
这个经过测试,当前版本是存在的,除非compact之后才会消失,而且用了5.1.4和5.4版本进行了测试
但是这个如何确认mvcc数据是否gc了,例如我insert一条数据,update了10次,那如何确认这9条mvcc数据是否被gc了?
如果可以确认被gc了,就方便查找是否在compact之前还存在这9条数据

问题5
集群开启老的gc是在11月23号,下面是记录的同一条sql的执行计划情况

11月30号,耗时 13.23 秒
total_keys: 31732179, rocksdb: {delete_skipped_count: 135, key_skipped_count: 31735728

12月1号,耗时 3.28 秒
total_keys: 7873311, rocksdb: {delete_skipped_count: 230, key_skipped_count: 7874853

12月2号,耗时 12.59 秒
total_keys: 13972030, rocksdb: {delete_skipped_count: 14220527, key_skipped_count: 41315019

12月5号,耗时 0.93 秒
total_keys: 2166513, rocksdb: {delete_skipped_count: 703, key_skipped_count: 2179817

这个是根据 gc_life_time 来判断的,gc 会清理 gc_life_time 时间戳之前的 mvcc 版本数据。目前查看 mvcc 版本方法通过 TiDB API 查询

执行计划除了 这部分耗时外,其他都相同么?通过这个 5 条记录看,每次的实际查询的 total key 的数据都不同的, process key 的数据是怎么样的,这个 SQL 查询的结果预期是怎么样的 ?

除了这部分耗时外,其他部分的耗时较少,就没做记录
total_process_keys由于基本上都是100左右,所以没做记录,下面是其中一次记录
12月6号,耗时 0.79 秒
total_process_keys: 173, total_keys: 1835642, rocksdb:{delete_skipped_count: 417, key_skipped_count: 1837387

我的理解:
1、 rocksdb使用iterator去扫描memtable、 sst,根据key进行prefix_seek定位,iterator会使用next从定位的起始key开始往下进行范围扫描,在此期间会碰到mvcc数据 、tombstone数据等。

2、skipped_count相关解释可以看下下面: delete_skipped_count 是rocksdb 表示删除后的tombstone key,这些在tikv是GC了的。key_skipped_count 包含delete_skipped和mvcc等的数量

When deleting a key, RocksDB simply puts a marker, called tombstone to memtable. The original value of the key will not be removed until we compact the files containing the keys with the tombstone. The tombstone may even live longer even after the original value is removed. So if we have lots of consecutive keys deleted, a user may experience slowness when iterating across these tombstones. Counters internal_delete_skipped_count tells us how many tombstones we skipped. internal_key_skipped_count covers some other keys we skip.

3、关闭 compaction-filter 后GC回到传统的模式,扫描数据然后在标记删除,这个过程也走raft,可能GC效率比 compaction-filter模式要低。

多谢答复。

v5.1.4 mvcc 测试

create table t123 (id int primary key,name varchar(100),age int,city varchar(100));
insert into t123 values (1,‘user1’,1,‘bj’);
update t123 set name=‘user2’ where id=1;
update t123 set name=‘user3’ where id=1;

[]# curl http://192.168.1.13:10080/mvcc/key/test/t123/1
{
“key”: “748000000000000C0B5F728000000000000001”,
“region_id”: 75055,
“value”: {
“info”: {
“writes”: [
{
“start_ts”: 437878836284620809,
“commit_ts”: 437878836284620812,
“short_value”: “gAADAAAAAgMEBQAGAAgAdXNlcjMBYmo=”
},
{
“start_ts”: 437878822036832288,
“commit_ts”: 437878822049939459,
“short_value”: “gAADAAAAAgMEBQAGAAgAdXNlcjIBYmo=”
},
{
“start_ts”: 437878822036832283,
“commit_ts”: 437878822036832284,
“short_value”: “gAADAAAAAgMEBQAGAAgAdXNlcjEBYmo=”
}
]
}
}
}

当前gc信息
select VARIABLE_NAME, VARIABLE_VALUE from mysql.tidb;
| tikv_gc_last_run_time | 20221207-08:56:56 +0800
| tikv_gc_safe_point | 20221207-08:46:56 +0800

等 gc 之后,再次查看 mvcc 数据,如下
select VARIABLE_NAME, VARIABLE_VALUE from mysql.tidb;
| tikv_gc_last_run_time | 20221207-09:16:56 +0800
| tikv_gc_safe_point | 20221207-09:06:56 +0800

[]# curl http://192.168.1.13:10080/mvcc/key/test/t123/1
{
“key”: “748000000000000C0B5F728000000000000001”,
“region_id”: 75055,
“value”: {
“info”: {
“writes”: [
{
“start_ts”: 437878836284620809,
“commit_ts”: 437878836284620812,
“short_value”: “gAADAAAAAgMEBQAGAAgAdXNlcjMBYmo=”
},
{
“start_ts”: 437878822036832288,
“commit_ts”: 437878822049939459,
“short_value”: “gAADAAAAAgMEBQAGAAgAdXNlcjIBYmo=”
},
{
“start_ts”: 437878822036832283,
“commit_ts”: 437878822036832284,
“short_value”: “gAADAAAAAgMEBQAGAAgAdXNlcjEBYmo=”
}
]
}
}
}

测试结果:
不符合预期,为什么gc之后,这行数据的mvcc数据还是存在的?

通过 TiDB MVCC API 查询到的数据版本是存在,实际 TiDB GC 的操作会将 safepoint 存到 PD 端,在 TiDB 侧可以视为已经 GC 完成了。但是实际 TiKV 会根据内部任务触发逻辑向 PD Sever 索要 safepoint ,然后根据 safepoint 再将旧的 MVCC 通过 Compaction 任务开始删除旧版本的 MVCC。所以这个是预期的情况。可以理解为删除旧版本的操作 TiDB 和 TiKV 共同完成以后,才视为删除。所以通过 API 查询看到 mvcc 没有清理,是预期的。
另外,因为每个 Region group 的 peer 都在不同 TiKV 实例,TiKV 实例的 GC 任务是异步的,所以可能有的旧 MVCC 版本已经清理,但是有的 peer 的旧版本还没有删除,没有办法保证已经比较的 safepoint 之前的快照是一致的。所以通常这里的旧版本的 MVCC 是保证一致性的。
Compaction Filter 开启后,gc 效率会比原来的高,是因为 Compactoin filter 承担部分之前 GC 单线程的任务操作,放在 TiKV 这一侧进行异步处理,这样可以让更多的 TIKV 实例到 gc_key 的操作中,加快处理速度。

https://github.com/tikv/tikv/issues/8486

  • 对多 Tombstones 场景下的写操作和 Batch Get 进行优化 #9729

可以看下这个

1 Like

多谢。