如何从kv数据结构映射到redis的复杂数据结构
如果底层是tikv和hbase(range分区),则在key前面增加slot前缀(对slot做md5,取前8个字节)
如果底层是obkv(hash分区),则slot作为obkv-table的hash分区的分区字段,key前面不需要加slot前缀
key
value
m# + namespace + key
1-bit + 1-bit + 8-bit + 8-bit + N-bit
prefix + namespace + key
encode-version + key-type + key-version + expire-time + extra
public enum KeyType {
string ((byte ) 1 ),
hash ((byte ) 2 ),
zset ((byte ) 3 ),
list ((byte ) 4 ),
set ((byte ) 5 ),
}
key-version使用创建key时的时间戳表示
expire-time记录key的过期时间戳,如果key没有ttl,则为-1
extra取决于encode-version和type,可选
key
value
m# + namespace + key
1-bit + 1-bit + 8-bit + 8-bit + N-bit
prefix + namespace + key
0 + 1 + key-version + expire-time + value
只有一种编码结构,encode-version固定为0
key-type固定为1
只有key-meta,没有sub-key
hash数据有2种编码模式
key
value
m# + namespace + key
1-bit + 1-bit + 8-bit + 8-bit + 4-bit
prefix + namespace + key
0 + 2 + key-version + expire-time + field-count
key
value
s# + namespace + key.len + key + key-version + field
field-value
encode-version固定为0,key-type固定为2
因为key-meta中记录了field-count,因此hlen快
hset/hdel返回结果准确
写操作的读放大多,因为每次写入都需要读一下是否是已经存在的field还是新的field,如hset操作,前者返回0,后者返回1
key
value
m# + namespace + key
1-bit + 1-bit + 8-bit + 8-bit
prefix + namespace + key
1 + 2 + key-version + expire-time
key
value
s# + namespace + key.len + key + key-version + field
field-value
encode-version固定为1,key-type固定为2
因为不在key-meta中记录field-count,纯覆盖写,写入快,但是导致了hlen慢
hset/hdel等操作返回结果不准确,比如hset操作,不管写入的是已存在的field还是新的field,都返回1
zset有2种编码结构
key
value
m# + namespace + key
1-bit + 1-bit + 8-bit + 8-bit + 4-bit
prefix + namespace + key
0 + 3 + key-version + expire-time + member-count
key
value
s# + namespace + key.len + key + key-version + member
score
key
value
k# + namespace + key.len + key + key-version + score + member
null
encode-version固定为0,key-type固定为3
不依赖任何redis
key-meta中记录了member-count,zcard快
写操作的读放大多,因为每次写入都需要读一下是否是已经存在的member还是新的member,如zadd操作,前者返回0,后者返回1
key
value
m# + namespace + key
1-bit + 1-bit + 8-bit + 8-bit
prefix + namespace + key
3 + 3 + key-version + expire-time
index=member.len < 15 ? (prefix1+member) : (prefix2+md5(member))
key
value
i# + namespace + key.len + key + key-version + index
member
如果member很小,则不会产生二级的index,只会在redis中写入,不会写入kv
redis-index-zset-store-key
redis-key
redis-type
redis-value
c# + namespace + key + key-version
zset
index-zset
index-zset中可能存的是index,也可能不是index,通过前缀的第一个字节来判断
这部分redis数据不允许换出
redis-index-member-cache-key
redis-key
redis-type
redis-value
c# + namespace + key + key-version + index
string
member
encode-version固定为1,key-type固定为3
依赖redis做复杂的zset操作
redis里可以只存部分数据,对于kv只有get/put/delete,没有scan操作
redis-index-zset-store-key不属于cache,属于storage,需要确保storage部分redis内存足够,否则可能被驱逐
在这个编码下,lex相关的命令(如zlexcount、zrangebylex等),需要先把所有元素load到本地内存中,再执行操作
set数据有2种编码模式
key
value
m# + namespace + key
1-bit + 1-bit + 8-bit + 8-bit + 4-bit
prefix + namespace + key
0 + 2 + key-version + expire-time + member-count
key
value
s# + namespace + key.len + key + key-version + member
nil
encode-version固定为0,key-type固定为2
因为key-meta中记录了member-count,因此scard快
sadd/srem返回结果准确
写操作的读放大多,因为每次写入都需要读一下是否是已经存在的member还是新的member,如sadd操作,前者返回0,后者返回1
key
value
m# + namespace + key
1-bit + 1-bit + 8-bit + 8-bit
prefix + namespace + key
1 + 2 + key-version + expire-time
key
value
s# + namespace + key.len + key + key-version + member
field-value
encode-version固定为1,key-type固定为2
因为不在key-meta中记录member-count,纯覆盖写,写入快,但是导致了scard慢
sadd/srem等操作返回结果不准确,比如sadd操作,不管写入的是已存在的member还是新的member,都返回1
todo