前言
如果我们的业务需要32GRedis实例,我们创建一个32GRedis实例还是有其他的解决方案呢?
Redis Cluster 从Redis 3.0开始支持使用,用于实现切片集群。
Redis Cluster 保证CAP中的可用性和一致性。
切片集群
为了支持大容量Redis提出的方案,多个Redis实例存储不同的key和value。Redis 官方提供的解决方案是Redis Cluster,哈希槽便是用来处理数据和实例之间的关系。
为什么不使用更大的内存的Redis?
内存更大,持久化和恢复时间越长;需要更大内存和更强CPU支持。
Redis Cluster
哈希槽
Redis Cluster 不使用一致性散列,而是一种不同形式的分片,其中每个key在概念上都是我们所谓的散列槽的一部分。
Redis 集群中有 16384 个哈希槽(2的14次方),要计算key的哈希槽是多少,我们只需计算CRC16(key) % 16384。
Redis实例和哈希槽关联
分配方式:
1.cluster create命令创建集群时,Redis把哈希槽平均分配给Redis实例,每个Redis实例分配哈希槽数量=16384/N(N为Redis实例数量);
2.cluster meet命令手动建立Redis实例间的连接,形成集群,再通过cluster addslots命令指定每个Redis实例哈希槽个数(如果Redis实例内存、硬件配置不同比较适用);
Client和cluster交互
client如何指定key对应那个Redis实例?
client与cluster连接后,Redis实例会把哈希槽信息发送给client。
cluster建立后Redis实例间相互连接,可以指定彼此分配到的哈希槽信息,所有client与一个Redis实例连接时可以知道所有Redis实例的哈希槽信息。
cluster重新分配哈希槽后,client如何更新Redis实例和哈希槽的映射关系呢?
client请求key时,先在本地计算key对应的哈希槽,再给对应的Redis实例发送key请求。
添加Redis实例、移除Redis实例、手动修改Redis实例与哈希槽的映射都会重新分配哈希槽,Redis实例可以相互连接通信指定彼此的哈希槽信息,但是client不知道Redis与哈希槽信息发送变更。
cluster 提供重定向机制,client发送key操作到Redis实例,并且key对应的哈希槽不属于当前Redis实例,那么便会返回MOVED命令响应client。
GET testKey1
(error) MOVED 13320 172.16.19.5:6379
client 重新向172.16.19.5:6379 发送key操作,并且重新更新Redis实例与哈希槽映射关系。
cluster添加Redis实例情况
cluster添加新Redis实例时,cluster重新分配哈希槽、属于新Redis实例的key从原来的Redis实例迁移过去。
迁移过程中,client需要操作key,会出现什么情况呢?
GET testKey1
(error) ASK 13320 172.16.19.5:6379
ASK命令表示key对应的哈希槽为13320,172.16.19.5:6379 Redis实例上,但是当前正在迁移。
client需要向172.16.19.5:6379实例发送ASKING命令,让实例允许client接下来的key操作,client再向172.16.19.5:6379实例发送GET testKey1 操作。
ASK命令和MOVED命令不同,ASK命令client不会更新本地Redis实例与哈希槽的映射缓存,ASK命令让client把key操作重新请求一次返回去的Redis实例。
Redis cluster多key操作支持
MULTI/EXEC事务命令、mget、mset等多key操作时,Redis是否支持?如支持同一个Redis节点处理还是离散到其他Redis节点处理?
Redis Cluster 支持多key操作,只要涉及到单个命令执行(或整个事务,或 Lua 脚本执行)的所有key都属于同一个哈希槽。用户可以使用称为散列标签的概念强制多个key成为同一个散列槽的一部分。
Redis cluster配置
添加节点
删除节点
Redis cluster问题
数据倾斜
如果发生了数据倾斜,那么保存了大量数据,或者是保存了热点数据的Redis实例的处理压力就会增大,速度变慢,甚至还可能会引起这个实例的内存资源耗尽,从而崩溃。在应用切片集群时要避免的。
数据量倾斜
Redis实例上的数据分布不均衡,某个实例上的数据特别多。
数据量倾斜是怎么产生的呢?
主要有三个原因:
1.分别是某个Redis实例上保存了 bigkey;
解决方案:生成缓存数据时,要尽量避免把过多的数据保存在同一个键值对中。例如一个对象,可以拆分为对个key保存,后者使用hash保存,每次取需要的属性;如果bigKey为hash,可以拆分成多个hash key。
2.集群Slot 分配不均衡;
如果管理员没有均衡地分配集群的Slot,可能出现大量的数据被分配到同一个 Slot 中,而同一个 Slot 只会在一个Redis实例上分布,这就会导致,大量数据被集中到一个实例上,造成数据倾斜。
执行CLUSTER SLOTS 命令查看 Slot 分配情况。
解决方案:管理员使用内存大小一致的Redis实例,slot平均发布。
- Hash Tag。
Hash Tag 是指加在key-value对 key 中的一对花括号{}。这对括号会把 key 的一部分括起来,client在计算 key 的 CRC16 值时,只对 Hash Tag 花括号中的 key 内容进行计算。如果没用 Hash Tag 的话,client计算整个 key 的 CRC16 的值。
假如key为:user:info:{713},713就是Hash Tag,client取713计算CRC16值。
数据访问倾斜
集群中的Redis实例上的数据量相差不大,但是某个实例上的数据是热点数据,被访问得非常频繁。例如app首页配置、秒杀商品信息、公共配置。
解决方案1:缓存数据多副本,缓存key增加后缀(1-3数字),访问时,随机生成1-3拼接在key上。
弊端是多副本,需要额外保证多副本数据一致。适用于固定配置、热点新闻等。
解决方案2:JVM本地短时间缓存,例如缓存1-30秒。
Redis实例上限
Redis 官方给出了 Redis Cluster 的规模上限,就是一个集群运行 1000 个实例。
前面说的集群中Redis实例相互通信,Redis实例间的通信开销会随着Redis实例规模增加而增大。在集群超过一定规模时(比如 800 节点),集群吞吐量反而会下降。所以,集群的实际规模会受到限制。
Gossip 协议是Redis实例间通信规则。
Gossip 协议的工作原理可以概括成两点:
1.每个Redis实例之间会按照一定的频率,从集群中随机挑选一些实例,把 PING 消息发送给挑选出来的实例,用来检测这些实例是否在线,并交换彼此的状态信息。PING 消息中封装了发送消息的实例自身的状态信息、部分其它实例的状态信息,以及 Slot 映射表。
2.一个Redis实例在接收到 PING 消息后,会给发送 PING 消息的实例,发送一个 PONG 消息。PONG 消息包含的内容和 PING 消息一样。
Gossip 消息大小
Redis 实例发送的 PING 消息的消息体是由 clusterMsgDataGossip 结构体组成的,这个结构体的定义如下所示:
typedef struct {
//节点的名字
char nodename[REDIS_CLUSTER_NAMELEN];
//最后一次向该节点发送PING消息的时间戳
uint32_t ping_sent;
//最后一次从该 节点接收到PONG消息的时间戳
uint32_t pong_received;
//节点的IP地址
char ip[16];
//节点的端口号
uint16_t port;
//节点的标识值
uint16_t flags;
} clusterMsgDataGossip;
参考:
https://redis.io/topics/cluster-tutorial