MyRocks TTL 简单用法如下,

在comment中通过ttl_duration指定过期时间,ttl_col指定过期时间列

也可以不指定过期时间列ttl_col,插入数据时会隐式将当前时间做为过期时间列存储到记录中。

  1. a bigint(20) NOT NULL,
  2. PRIMARY KEY (a)
  3. ) ENGINE=rocksdb
  4. COMMENT='ttl_duration=1;';

分区表也同样支持TTL

RocksDB TTL

介绍MyRocks TTL实现之前,先来看看RocksDB TTL。
RocksDB 本身也支持TTL, 通过DBWithTTL::Open接口,可以指定每个column_family的过期时间。

在Compact时通过自定义的TtlCompactionFilter , 去判断数据是否可以清理。具体参考DBWithTTLImpl::IsStale

  1. bool DBWithTTLImpl::IsStale(const Slice& value, int32_t ttl, Env* env) {
  2. if (ttl <= 0) { // Data is fresh if TTL is non-positive
  3. return false;
  4. int64_t curtime;
  5. if (!env->GetCurrentTime(&curtime).ok()) {
  6. return false; // Treat the data as fresh if could not get current time
  7. }
  8. DecodeFixed32(value.data() + value.size() - kTSLength);
  9. return (timestamp_value + ttl) < curtime;
  10. }

和RocksDB TTL column family级别指定过期时间不同,MyRocks TTL可表级别指定过期时间。
MyRocks TTL表过期时间存储在数据字典INDEX_INFO中,表中可以指定过期时间列ttl_col, 也可以不指定, 不指定时会隐式生成ttl_col.

对于主键,ttl_col的值存储在value的头8个字节中,对于指定了过期时间列ttl_col的情况,value中ttl_col位置和valule的头8个字节都会存储ttl_col值,这里有一定的冗余。具体参考convert_record_to_storage_format

读取数据会自动跳过ttl_col占用的8个字节,参考convert_record_from_storage_format

二级索引ttl_col同主键保持一致。 对于更新显式指定的ttl_col列时,所有的二级索引都需要更新,即使此列不在二级索引列中

MyRocks TTL 清理

MyRocks TTL 清理也发生在compact时,由Rdb_compact_filter定义清理动作, 具体参考should_filter_ttl_rec

RocksDB TTL中过期时间和当前时间做比较,而MyRocks TTL 的过期时间是和最老的快照时间(m_snapshot_timestamp )做比较(当没有快照时,也取当前时间)。

  1. bool should_filter_ttl_rec(const rocksdb::Slice &key,
  2. const rocksdb::Slice &existing_value) const {
  3. uint64 ttl_timestamp;
  4. Rdb_string_reader reader(&existing_value);
  5. if (!reader.read(m_ttl_offset) || reader.read_uint64(&ttl_timestamp)) {
  6. std::string buf;
  7. RDB_MAX_HEXDUMP_LEN);
  8. // NO_LINT_DEBUG
  9. "for index (%u,%u), val: %s",
  10. m_prev_index.cf_id, m_prev_index.index_id, buf.c_str());
  11. abort();
  12. }
  13. /*
  14. Filter out the record only if it is older than the oldest snapshot
  15. timestamp. This prevents any rows from expiring in the middle of
  16. long-running transactions.
  17. */
  18. return ttl_timestamp + m_ttl_duration <= m_snapshot_timestamp;

前面讲到, RocksDB TTL 过期时间并不严格,取决于compaction速度。MyRocks TTL也有类似问题,因此MyRocks引入参数rocksdb_enable_ttl_read_filtering, 当开启此参数时,过期时间是严格的。 每次读取记录会调用should_hide_ttl_rec判断此记录是否过期,当compact操作不及时而没有清理的过期记录,在读取时会被过滤掉。

MyRocks TTL 潜在问题

中谈到了MyRocks TTL 有个潜在问题, 当更新显式指定的ttl_col列值时,compact时有可能将新的记录清理掉,而老的记录仍然保留,从而有可能读取到本该不可见的老记录。此问题暂时还没有close.