Redis集群遇到的一些问题

我们在使用Redis集群的使用过程中发现了很多问题

这些问题产生的原因归根到底都是因为redis将数据映射到槽(slot)上面, 而不同的键分布在不同的Node节点上面导致的

键分布导致的问题

当在集群模式下进行多键操作, 同时操作的键中有部分不在该节点时,会报如下错误

CROSSSLOT Keys in request don't hash to the same slot

例如:

hmget key1 key2

rename key1 key2

错误类型

  • moved错误

该错误表明当前键的落点槽不属于当前Node, 一般之后会跟着一个槽落点和节点地址

例如:

MOVED 1584 10.200.6.185:7001

  1. 基于该错误可以调整客户端, 让不支持集群模式的客户端也能支持集群功能

  2. 也可以提前计算好key, 直接去负责该key所在槽的服务器上取数据

  • ASK错误

该错误表明该key对应的数据正在做数据迁移, 槽迁移会引起该槽上的数据返回该错误

集群模式下不支持select db

单机模式我们可以使用 select 1 来选择使用的数据库(redis默认提供16个数据库)

但是集群模式下redis不支持该功能

如果不同的业务组之间需要做业务隔离最好采用不同redis集群的形式进行

可以把多个redis集群组成redis组,多个redis组组成redis池进行资源的统一管理

cluster模式不支持使用Pipeline

在缓存的使用过程中,有时候我们会有快速获取一批k-v存储结果的需求,比如首页列表产品页,此时如果我们对每一个记录使用get操作在数据量较大的时候将会导致整个请求时间过长难以接受

Redis的pipeline是一种效果很明显的加快获取数据速度的手段,我们可以用pipeline一次性读取多个key的值

像这样

1
2
3
4
5
6
7
8
$ batch 

$ get a
$ get b
$ get c
$ ....

$ exec

但是有一个缺点, 我们一旦使用了Redis的Cluster集群, 就没有办法对整个集群使用Pipeline功能

原因

因为Redis的Pipeline的原理是在一个连接中持续的进行命令发送, 需要持有一个稳定的连接

但是一旦使用了集群, 我们每个连接只能连接一个Node节点, 可是Pipeline中发送的数据键的数据落点未必会落到我们当前连接的集群节点上去

那经过Pipeline发送过去的key就很明显不能正确的拿到内容

解决方案1

  • 我们先计算出来每个key的数据落点, 然后将key进行分组, 分组取数据

步骤:

  1. 实现集群节点选择方法, 给每个节点起一个名字, 例如 节点1, 节点2, 节点3

  2. 查找每个节点上面的数据槽(slot)的范围

  3. 对Pipeline中的每个key进行crc16(key)&16383, 计算出来每个key的槽, 并找到槽对应的节点

  4. 将落到相同节点的key进行分组

  5. 对每一组key再进行Pipeline操作

优点

充分利用集群特性, 普适性好, 不会造成数据倾斜

缺点

按照节点计算key, 最大请求次数等于节点数, 节点太多的情况下不太合适, 适合小规模集群

解决方案2

  • 存储时候就把key存到一起

使用{}来对key进行标记

例如: {user}:name:3, {user}:name,{user}:age

这三个key因为{}部分都相同所以会落到同一个slot上面, 数据自然就落到同一台redis机器上面了

优点

不受集群规模和节点数量的影响

缺点

但是这种方法限制很大, 没办法充分利用Redis的集群特性, 仅仅适合使用比较频繁的小数据量

数据量太大会导致大量数据存储在同一个槽内, 造成数据倾斜