基于camellia-redis-proxy的可插拔架构设计,支持对接外部kv存储,模拟redis协议
proxy基于redis-cluster模式运行,因此相同key会路由到同一个proxy节点(proxy节点扩缩容时需要精细化处理,todo-optimize)
proxy内部多work-thread运行,每个命令根据key哈希到同一个work-thread运行
proxy本身弱状态
proxy依赖的服务逻辑上包括三组:key-meta-server、sub-key-server、redis-cache-server(可选)、redis-storage-server(可选)
key-meta-server,用于维护key的meta信息,包括key的类型、版本、ttl等,可以基于hbase/tikv/obkv实现,可以前置redis-cache-server
sub-key-server,用于存储hash中的field等subkey,可以基于hbase/tikv/obkv实现,可以前置redis-cache-server
部分场景下,可以在sub-key-server层,混合使用redis作为storage,而非完全的cache,来提升性能
对于hbase/tikv/obkv的访问有一个抽象层,也可以替换为其他kv存储
参考了 pika 、 kvrocks 、 tidis 、 titan 、 titea 的设计
使用gc机制来回收kv存储层的过期数据,具体见: gc
key
value
m# + namespace + md5(key)[0-8] + key
1-bit + 1-bit + 8-bit + 8-bit + N-bit
prefix + namespace + md5_prefix + 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-meta本身支持配置redis-cache-server,从而加快读写(可换出)
key-version使用创建key时的时间戳表示
expire-time记录key的过期时间戳,如果key没有ttl,则为-1
extra取决于encode-version和type,可选
# key-meta是否开启缓存,默认false
kv.key.meta.cache.enable =false
# key-meta开启缓存时的ttl,默认10分钟
kv.key.meta.cache.millis =600000
command
info
del
DEL key [key ...]
exists
EXISTS key [key ...]
expire
EXPIRE key seconds [NX | XX | GT | LT]
pexpire
PEXPIRE key milliseconds [NX | XX | GT | LT]
expireat
EXPIREAT key unix-time-seconds [NX | XX | GT | LT]
pexpireat
PEXPIREAT key unix-time-milliseconds [NX | XX | GT | LT]
unlink
UNLINK key [key ...]
type
TYPE key
ttl
TTL key
pttl
PTTL key
expiretime
EXPIRETIME key
pexpiretime
PEXPIRETIME key
key
value
m# + namespace + md5(key)[0-8] + key
1-bit + 1-bit + 8-bit + 8-bit + N-bit
prefix + namespace + md5_prefix + key
0 + 1 + key-version + expire-time + value
只有一种编码结构,encode-version固定为0
key-type固定为1
只有key-meta,没有sub-key
没有专门的缓存结构,依赖于key-meta本身的缓存
command
info
setex
SETEX key seconds value
psetex
PSETEX key milliseconds value
set
SET key value [NX | XX] [GET] [EX seconds | PX milliseconds | EXAT unix-time-seconds | PXAT unix-time-milliseconds | KEEPTTL]
get
GET key
mget
MGET key [key ...]
mset
MSET key value [key value ...]
setnx
SETNX key value
hash数据有四种编码模式
# #四种编码模式,0、1、2、3,默认0
kv.hash.key.meta.version =0
# #在2和3这两种编码下,hget缓存的ttl,默认5分钟
kv.cache.hget.cache.millis =300000
# #在2和3这两种编码下,hgetall缓存的ttl,默认5分钟
kv.cache.hgetall.cache.millis =30000
key
value
m# + namespace + md5(key)[0-8] + key
1-bit + 1-bit + 8-bit + 8-bit + 4-bit
prefix + namespace + md5_prefix + 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 + md5(key)[0-8] + key
1-bit + 1-bit + 8-bit + 8-bit
prefix + namespace + md5_prefix + 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
version-2同version-0,但是encode-version固定为2
version-3同version-1,但是encode-version固定为3
version-2同version-0
version-3同version-1
相比version-0和version-1,新增了redis缓存层
redis-key
redis-type
redis-value
c# + namespace + key + key-version + field
string
field-value
redis-key
redis-type
redis-value
c# + namespace + key + key-version
hash
full-hash
command
info
hset
HSET key field value [field value ...]
hmset
HMSET key field value [field value ...]
hget
HGET key field
hmget
HMGET key field [field ...]
hdel
HDEL key field [field ...]
hgetall
HGETALL key
hlen
HLEN key
hkeys
HKEYS key
hvals
HVALS key
zset有5种编码结构
# #4种编码模式,0、1、2、3,默认0
# #0这种编码结构,需要底层kv存储支持reverse-scan,当前tikv和obkv不支持
kv.zset.key.meta.version =0
kv.cache.zset.member.cache.millis =300000
kv.cache.zset.range.cache.millis =300000
key
value
m# + namespace + md5(key)[0-8] + key
1-bit + 1-bit + 8-bit + 8-bit + 4-bit
prefix + namespace + md5_prefix + key
0 + 3 + key-version + expire-time + member-count
key
value
s# + namespace + md5(key)[0-8] + key.len + key + key-version + member
score
key
value
k# + namespace + md5(key)[0-8] + 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 + md5(key)[0-8] + key
1-bit + 1-bit + 8-bit + 8-bit + 4-bit
prefix + namespace + md5_prefix + key
1 + 3 + key-version + expire-time + member-count
key
value
s# + namespace + md5(key)[0-8] + key.len + key + key-version + member
score
redis-key
redis-type
redis-value
c# + namespace + key + key-version
zset
full-zet
encode-version固定为1,key-type固定为3
依赖redis做复杂的zset操作
当redis过期时,需要从kv中全量导出所有数据重建cache
key
value
m# + namespace + md5(key)[0-8] + key
1-bit + 1-bit + 8-bit + 8-bit + 4-bit
prefix + namespace + md5_prefix + key
0 + 3 + key-version + expire-time + member-count
index=member.len < 15 ? (prefix1+member) : (prefix2+md5(member))
key
value
s# + namespace + md5(key)[0-8] + key.len + key + key-version + member
score
key
value
i# + namespace + md5(key)[0-8] + key.len + key + key-version + index
member
redis-index-zset-cache-key
redis-key
redis-type
redis-value
c# + namespace + key + key-version
zset
index-zet
redis-index-member-cache-key
redis-key
redis-type
redis-value
c# + namespace + key + key-version + index
string
member
encode-version固定为2,key-type固定为3
依赖redis做复杂的zset操作
redis纯缓存使用,可以换出,且redis里可以只存部分数据
当redis过期时,需要从kv中导出数据重建cache(可以只导出index)
key
value
m# + namespace + md5(key)[0-8] + key
1-bit + 1-bit + 8-bit + 8-bit
prefix + namespace + md5_prefix + key
3 + 3 + key-version + expire-time
index=member.len < 15 ? (prefix1+member) : (prefix2+md5(member))
key
value
i# + namespace + md5(key)[0-8] + 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固定为3,key-type固定为3
依赖redis做复杂的zset操作
redis里可以只存部分数据,对于kv只有get/put/delete,没有scan操作
redis-index-zset-store-key不属于cache,属于storage,需要确保storage部分redis内存足够,否则可能被驱逐
command
info
zadd
ZADD key score member [score member ...]
zcard
ZCARD key
zrange
ZRANGE key start stop [WITHSCORES]
zrangebyscore
ZRANGEBYSCORE key min max [WITHSCORES] [LIMIT offset count]
zrangebylex
ZRANGEBYLEX key min max [LIMIT offset count]
zrevrange
ZREVRANGE key start stop [WITHSCORES]
zrevrangebyscore
ZREVRANGEBYSCORE key min max [WITHSCORES] [LIMIT offset count]
zrevrangebylex
ZREVRANGEBYLEX key min max [LIMIT offset count]
zremrangebyrank
ZREMRANGEBYRANK key start stop
zremrangebyscore
ZREMRANGEBYSCORE key min max
zremrangebylex
ZREMRANGEBYLEX key min max
zrem
ZREM key member [member ...]
zscore
ZSCORE key member
todo
todo