一些业务场景是经过一段时间后删除过期的数据,MyRocks提供了TTL可以满足该场景。MyRocks 通过Compaction回收过期的数据。本文介绍MyRocks TTL使用姿势以及Bugfix。
TTL 通过 table comment 定义,有两种形式:
CREATE TABLE t1 (a INT, b INT, c INT, PRIMARY KEY (a), KEY(b)) ENGINE=ROCKSDB COMMENT "ttl_duration=3600;";CREATE TABLE t2 (a INT, b INT, c INT, ts BIGINT UNSIGNED NOT NULL, PRIMARY KEY (a), KEY(b)) ENGINE=ROCKSDB COMMENT "ttl_duration=3600;ttl_col=ts;";
ttl_duration: 指定过期时间,单位是秒
ttl_col : 指定过期时间列,列的数据类型必须是 bigint unsigned not null, 不能是datetime类型。
表t1没有指定ttl_col,record插入的时间作为created time 会记录在record中,注意更新(update)此记录不会更新created time。表t2 显式指定了ttl_col,created time 直接从ttl_col列获取。
MyRocks 在读数据时会判断表是否有TTL定义,是否开启了rocksdb_enable_ttl_read_filtering(默认是开启的),如果都满足则会读取created time,判断是否过期。
实际使用中还碰到了一个TTL的bug,该bug是由Percona小伙伴提出来的,我们分析修掉了该Bug,如果表定义了TTL,并且有varchar字段,执行一些聚合查询会导致mysqld crash。重现该bug的方法如下:
CREATE TABLE `t1` (
`a` bigint(20) NOT NULL,
`b` varchar(10) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL,
`u` bigint(20) unsigned NOT NULL,
`d` bigint(20) DEFAULT NULL,
PRIMARY KEY (`a`,`b`),
KEY `d` (`d`)
) ENGINE=ROCKSDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin COMMENT='ttl_duration=1000;ttl_col=u';
INSERT INTO t1 VALUES (100, 'aaabbb', UNIX_TIMESTAMP(), 200);
EXPLAIN SELECT COUNT(*) FROM t1;
SELECT COUNT(*) FROM t1;
执行以上语句会导致mysqld crash,主要原因是在解析二级索引元组的时候忽略了TTL隐藏字段。如果表没有定义TTL,二级索引record的KV格式如下:
key: index number, NULL-flag, M(c), M(a)
value: empty or restore data
如果表定义了TTL字段,二级索引record的KV格式如下:
key: index number, NULL-flag, M(c), M(a)
value: timestamp or restore data
因为RocksDB存储的是KV格式的数据,MyRocks handler层负责把行记录格式的数据转成KV格式的数据,这个过程叫做pack,在查询的时候,再把底层KV格式的数据转换成行格式,这个过程叫做unpack。如果行记录中有特殊字段比如varchar、blob等,value还会存储unpack info,主要是为了能恢复出原来的varchar、blob。
该bug主要是在unpack_record函数中出错,

reader 保存二级索引KV中的key, unp_reader保存二级索引KV中的value,

注释中也说明了会按照unpack data 、checksum的顺序解析,但是如果表定义了TTL,value开始的8字节存储的是TTL时间戳,这么解析就出错了。解决的办法也很简单,就是要跳过TTL时间戳,

参考链接:
1、官方wiki: https://github.com/facebook/mysql-5.6/wiki/Time-to-Live-(TTL)
2、bug issue: https://github.com/facebook/mysql-5.6/issues/896
3、bug patch : https://github.com/facebook/mysql-5.6/pull/898