作者:vivo 互联网服务器团队- Yuan Jian Wei从外部需要登程,咱们基于TiKV设计了一款兼容Redis的KV存储。基于TiKV的数据存储机制,对于窗口数据的解决以及过期数据的GC问题却成为一个难题。本文心愿基于从KV存储的设计开始解说,到GC设计的逐层优化的过程,从问题的存在到不同层面的剖析,能够给读者在相似的优化实际中提供一种参考思路。
一、背景介绍以后公司外部没有对立的KV存储服务,很多业务都将 Redis 集群当作KV存储服务在应用,然而局部业务可能不须要 Redis 如此高的性能,却承当着微小的机器资源老本(内存价格绝对磁盘来说更加低廉)。为了升高存储老本的需要,同时尽可能减少业务迁徙的老本,咱们基于 TiKV 研发了一套磁盘KV存储服务。
1.1 架构简介以下对这种KV存储(下称磁盘KV)的架构进行简略形容,为后续问题形容做铺垫。
1.1.1 零碎架构磁盘KV应用目前较风行的计算存储拆散架构,在TiKV集群下层封装计算层(后称Tula)模仿Redis集群(对外体现是不同的Tula负责某些slot范畴),间接接入业务Redis客户端。
图1:磁盘KV架构图示
业务写入数据基于Tula转换成TiKV中存储的KV对,基于相似的形式实现业务数据的读取。
留神:Tula中会选举出一个leader,用于进行一些后台任务,后续具体说。
1.1.2 数据编码TiKV对外提供的是一种KV的读写性能,然而Redis对外提供的是基于数据结构提供读写能力(例如SET,LIST等),因而须要基于TiKV现有提供的能力,将Redis的数据结构进行编码,并且能够不便地在TiKV中进行读写。
TiKV提供的API比较简单:基于key的读写接口,以及基于字典序的迭代器拜访。
因而,Tula层面基于字典序的机制,对Redis的数据结构基于字典序进行编码,便于拜访。
留神:TiKV的key是能够基于字典序进行遍历(例如基于某个前缀进行遍历等),后续的编码,机制根本是基于字典序的个性进行设计。
为了能够更好地基于字典序排列的搜寻个性下对数据进行读写,对于简单的数据结构,咱们会应用另外的空间去寄存其中的数据(例如SET中的member,HASH中的field)。而对于简略的数据结构(相似STRING),则能够间接寄存到key对应的value中。
为此,咱们在编码设计上,会分为metaKey和dataKey。metaKey是基于用户间接拜访的key进行编码,是编码设计中间接对外的一层;dataKey则是metaKey的存储指向,用来寄存简单数据结构中的具体外部数据。
另外,为了保障拜访的数据一致性,咱们基于TiKV的事务接口进行对key的拜访。
(1)编码&字段
以下以编码中的一些概念以及设定,对编码进行简述。
key的编码规定如下:
图2:key编码设计图示
以下对字段进行阐明
namespace为了不便在一个TiKV集群中能够寄存不同的磁盘KV数据,咱们在编码的时候增加了前缀namespace,不便集群基于这个namespace在同一个物理TiKV空间中基于逻辑空间进行分区。
dbid因为原生Redis是反对select语句,因而在编码上须要预留字段示意db的id,不便后续进行db切换(select语句操作)的时候切换,示意不同的db空间。
role用于辨别是哪种类型的key。
对于简略的数据结构(例如STRING),只须要间接在key上面存储对应的value即可。
然而对于一些简单的数据结构(例如HASH,LIST等),如果在一个value下把所有的元素都存储了,对与相似SADD等指令的并发,为了保证数据一致性,必然能够预感其性能有余。
因而,磁盘KV在设计的时候把元素数据依照独立的key做存储,须要基于肯定的规定对元素key进行拜访。这样会导致在key的编码上,会存在key的role字段,辨别是用户看到的key(metaKey),还是这种元素的key(dataKey)。
其中,如果是metaKey,role固定是M;如果是dataKey,则是D。
keyname在metaKey和dataKey的根底上,能够基于keyname字段能够较不便地拜访到对应的key。
suffix针对dataKey,基于不同的Redis数据结构,都须要不同的dataKey规定进行反对。因而此处须要预留suffix区间给dataKey在编码的时候预留空间,实现不同的数据类型。
以下基于SET类型的SADD指令,对编码进行简略演示:
图3: SADD指令的编码设计指令图示
基于userKey,通过metaKey的拼接形式,拼接metaKey并且拜访拜访metaKey获取value中的基于value中的uuid,生成须要的dataKey写入生成的dataKey(2)编码实战
编码实战中,会以SET类型的实现细节作为例子,形容磁盘KV在实战中的编码细节。
在这之前,须要对metaKey的局部实现细节进行理解
(3)metaKey存储细节
所有的metaKey中都会存储下列数据。
图4:metaKey编码设计图示
uuid:每一个metaKey都会有一个对应的uuid,示意这个key的惟一身份。create_time:保留该元数据的创立工夫update_time: 保留该元数据的最近更新工夫expire_time: 保留过期工夫data_type: 保留该元数据对应的数据类型,例如SET,HASH,LIST,ZSET等等。encode_type: 保留该数据类型的编码方式(4)SET实现细节
基于metaKey的存储内容,以下基于SET类型的数据结构进行解说。
SET类型的dataKey的编码规定如下:
keyname:metaKey的uuidsuffix:SET对应的member字段因而,SET的dataKey编码如下:
图5:SET数据结构dataKey编码设计图示
以下把用户能够拜访到的key称为user-key。汇合中的元素应用member1,member2等标注。
这里,能够梳理出拜访逻辑如下:
图6:SET数据结构拜访流程图示
简述上图的拜访逻辑:
...