数据分片规则
在集群版架构中,虽然 Proxy 为客户端屏蔽了集群的实现细节,但目前尚不能做到和使用单机版完全一样,本节我将描述集群版的一些限制及其原因,并给出一些替代方案。
数据分片规则
云数据库 Redis 采用基于代理的 Redis 集群方案,客户端连接经由负载均衡器连接到 Proxy 上,后续客户端的请求将有 Proxy 负责路由到对应的Redis分片上。其结构如下:
集群版中,将全部的数据空间划分为 16384 个子空间,工程上我们称其为 slot,即一共有 16384 个 slot,这些 slot 分散在多个分片中。 对一个 KV 对,按如下规则将其映射到某个分片上:
slot = crc16(key) % 16384
shard = slot_to_shard_mapping[slot]
对于每一个集群,都存在一个与之关联的 slot 到 shard 的映射表,这个表由集群管控组件维护。用户执行命令后,Proxy 会拿到这个命令的 key,并计算 slot,而后使用 slot 查表得到对应分片。 客户端无需关注集群的结构,通过 Proxy 即可访问云数据库 Redis 集群,就像是在访问单机版 Redis 一样。但因为数据被分散在了多个分片中,在命令执行上和单机版 Redis 尚存在一些差异。
如何保证多个 key 落在同一个分片?
在设计业务方案时候,用户明确某些 key 之间存在关联,希望其放置在同一个分片中。但总不能选择一组 key 保证它们执行 crc16 后得到的结果相同吧。当然不用,此时可以使用 hashtag。
hashtag 就是在 key 中使用 {} 花括号包含一个字符串,Proxy 在对此 key 计算 hash 值的时候,只会使用 {} 中的内容,如此就可以保证不同的 key 计算出的 哈希值完全一样了,下面是个例子:
key_00000000000 ---> crc16 ---> % 16384 ---> 5480
key_11111111111 ---> crc16 ---> % 16384 ---> 15248
{key}_00000000000 ---> crc16 ---> % 16384 ---> 12539 # 将 key 包含在 {} 中作为 hashtag
{key}_11111111111 ---> crc16 ---> % 16384 ---> 12539
对存在关联的 key 使用相同的 hashtag,可以保证这一组 key 映射到相同的 slot 中,进而保证其落在同一个分片上。
注意事项:
- hashtag 不限定出现的位置,可以在 key 中任何位置。
- 如果key出现了多个 hashtag,只使用第一个出现的 hashtag 来计算哈希值。
- 不要滥用 hashtag,如果有大量的 key 使用了相同的 hashtag,那么某个分片中就会比其他分片多很多 key,那么这会导致各分片的数据量不均衡,进而导致某些 Redis 实例的压力偏大。
多 key 命令的使用限制
对于 DEL、MGET、MSET 这类常用的涉及多个 key 的操作,我们在 Proxy 层做了处理,会自动将命令进行拆分,并将拆分后的子命令发送给对应的分片,而后对结果进行汇总。这类命令包含下面这些:MGET、MSET、DEL、UNLINK、EXISTS、TOUCH,使用这些命令时,即便这些命令涉及的 key 落在不同的分片上,用户也无需特殊处理。
其他部分Redis 命令涉及到同时处理多个 key,比如 RENAME 命令,此命令用于将一个 key 重命名,比如下面的命令将 A 重命名为 B:
RENAME A B
对于 Redis 集群版而言,假如 A 和 B 这两个 key 落在不同的分片上。想完美地实现 RENAME 同等功能,Proxy 需要把 A 取出来,然后将其写入到 B 中,这将是一个很复杂的操作,而且会引发很多其他的问题。
实际上,Proxy 只是基于 A 来执行转发逻辑,将RENAMEAB 命令发送给A对应的分片。当RENAME执行完毕后,在分片 0 中就会多出一个 B key:
当用户尝试读取B时候,会将命令发送到分片1中,但B实际上在0分片,而这个key则不会被读取到。 对于这种涉及到多个 key 的命令,用户需要保证多个 key 落在同一个分片中。