TTL

原理

Pegasus支持TTL(Time-To-Live)功能,即在写入数据的时候,可以指定数据的过期时间。一旦过期,数据对用户就是不可见的,通过get/multiGet等查询接口获取不到数据,就跟数据没有写入一样。

设置的时候,用户通常都是提供ttl_seconds参数,表示从当前时间开始计算,多少秒之后为过期时间。如果为0,表示不设置TTL,即数据用不过期。

用户通常有疑问,数据过期后对用户不可见是怎么做到的呢?数据会被立即删除吗?实际上不是这样的,这要从TTL的实现原理说起。

Pegasus的TTL是通过在RocksDB中存储数据时记录数据的过期时间,然后在查询时对过期时间进行检查和过滤来实现的。如下图:

pegasus-ttl.png

写入过程:

  • 在写入数据时,用户在客户端通过ttl_seconds参数设置TTL时间,客户端先计算数据的过期时间ExpireTime = CurrentTime + ttl_seconds,然后通过RPC将数据和ExpireTime一起传给ReplicaServer端执行。
  • ReplicaServer收到写请求后,经过各种处理(包括写日志、replication复制等),最后将数据存储到RocksDB中。在存储value的时候,会将ExpireTime放在value头部固定的4个字节中。

读取过程:

  • 用户通过客户端查询指定key对应的value数据。
  • ReplicaServer收到读请求后,先从RocksDB获取到key对应的value,然后从value头部提取出ExpireTime:
    • 如果ExpireTime == 0,表示数据没有设置TTL,是有效的。
    • 如果ExpireTime > 0,表示数据设置了TTL,则将ExpireTime与当前时间进行对比:如果没有过期,则数据是有效的;如果已经过期,则数据是无效的,返回NotFound。

数据删除:

  • 数据过期后,并不能立即从RocksDB中消失,而是通过compaction来进行删除的。我们定制了RocksDB的CompactionFilter,使其在compaction过程中检查数据value头部的ExpireTime,如果已经过期,则数据会被扔掉,不会出现在新生成的文件中。
  • 因为删除过程是异步的,与compaction的执行时机和频率有关,所以数据过期与数据删除通常不是同时发生的,唯一能保证的是数据删除肯定发生在数据过期之后。未删除的过期数据会占据磁盘空间,这点是需要考虑到的。

接口

我们在客户端和Shell工具都提供了设置和查询TTL的接口。

Pegasus Java Client中以下接口可以查询和设置TTL:

  • ttl:获取指定数据的TTL信息。
  • setbatchSet:都提供了设置TTL的参数,其中batchSet是在SetItem中设置的。
  • multiSetbatchMultiSet:都提供了设置TTL的参数。
  • incr:从v1.11.1版本开始,incr接口也提供了修改TTL的功能。
  • checkAndSet:在CheckAndSetOptions中提供了设置TTL的参数。

Shell工具中以下命令可以查询和设置TTL:

  • ttl命令:获取指定数据的TTL信息。
  • setmulti_set命令:都提供了设置TTL的参数。

表级TTL

v1.11.2版本开始,Pegasus支持表级TTL功能。

实现原理:

  • 用户在Table环境变量中设置default_ttl环境变量。
  • MetaServer将环境变量异步地通知到各个ReplicaServer,使该表的每个replica都获取到该环境变量,这个过程大约有几秒到几十秒不等的延迟,但是不会超过一分钟。
  • replica获得环境变量后,解析获得default_ttl配置,并立即开始生效。生效之后:
    • 用户新写入的数据,如果TTL=0(使用默认TTL=0或者显式设置TTL=0),则将数据的实际TTL设置为default_ttl。
    • RocksDB在进行compaction的时候,如果compact输入文件的原数据没有TTL,则将compact输出文件的新数据的TTL设置为default_ttl。这个过程依赖于compaction的触发时机,所以时间点是不确定的。
    • 如果执行Manual Compact,那么所有文件都会经过compaction处理,原来没有TTL的数据都会设置TTL为default_ttl。

考虑这样的场景:业务方在初期写入数据时没有设置TTL,后来改变需求,希望所有数据都加TTL,并且以前没有设置TTL的数据从现在开始计算TTL,那么就可以通过表级TTL加上Manual Compact的功能实现这个目的。

通过TTL计算数据写入时间

如果数据写入时设置了TTL,就可以通过TTL计算出数据写入时间。依据的公式是:

TTLExpireTime = InsertTime + TTLSeconds = now + TTLRemainingSeconds

  ==>

InsertTime = now + TTLRemainingSeconds - TTLSeconds

其中:

  • TTLRemainingSeconds:通过Shell的ttl命令获取。
  • now:执行Shell ttl命令的时间。
  • TTLSeconds:用户知道数据写入时设置的TTL。