pika的持久化存储模块称为nemo存储引擎,其本质上是对rocksDB(只支持KV存储)的改造和封装。使其支持多数据结构的存储。
pika作为类redis数据库,所以肯定得兼容redis最基本的五种数据结构:string、hash、list、set、zset。
KV存储
KV存储作为rocksDB原生支持的存储方式,所以并没有做太多的处理。仅仅只是在value的结尾加上8个字节的附加信息(前4个字节表示version,后4个字节表示ttl)。
version字段用于对该键值对进行标记,以便后续处理,如删除一个键值对时,可以在该version进行标记,后续再进行真正的删除,这样可以减少删除操作造成的服务阻塞时间。
Hash存储
对于每一个Hash结构,它都包含hash键(key)、域名(field)、值(value)。
nome的存储方式是将key和field组合成为一个新的key,将这个新生成的key与要存储的value组成最终落盘的kv键值对。
对于每一个hash键,nome还为它添加了一个存储元数据信息的落盘kv,它保存的是对应hash键下的所有域值对的个数。
- 左图字段保存的是hash键的对象,它由标记位+key+version+ttl组成
- 右图字段仅仅只保存一个数字,表示该hash下field的数量
上图表示nome对传统hash结构转换成kv结构的拆分存储模式。
List存储
每个List结构的底层存储也是采用链表结构来完成,对于每个List键,它的每个元素都落盘成一个kv键值对。
和Hash结构一样,每个List也需要拥有一个它的元素信息结构。
- List的key存储和hash的一样,只有标记位不一样而已。
- value则存储了List的元数据信息,它有四个字段,从前到后分别为该List键内的元素个数、最左边元素节点的sequence(表头)、最右边元素节点的sequence(表尾)、下一个插入元素节点应该使用的sequence。
- k结构部分由标记位+长度+key+sequence组成。size表示的是hash键key的长度。
- v结构部分由前一个元素的sequence+后一个元素的sequence+value+version+ttl组成。从而实现了一个双向链表的结构。
Set存储
Set,本质上就是一个value值为nil的Hash结构。在Java和Redis中,都是利用hashtable来实现的。nome并没有做什么特殊处理,只是在存储value时,只保存了version和ttl结构。
Zset存储
Zset就是一个有序的Set结构,所以对于每个元素,增加了一个scope值。把该元素对应的score值整合进去,这样便于依据Score值进行排序(因为从rocksdb内拿出的数据时按键排序的)
score是从double类型转变过来的int64_t类型,这样做是为了可以让原来的浮点型的score直接参与到字符串的排序当中(浮点型的存储格式与字符串的比较方式不兼容)。