Redis Cluster通过hash slot映射数据,但是如何把两个key映射到同一个slot中呢?

本文涉及的产品
云数据库 Tair(兼容Redis),内存型 2GB
Redis 开源版,标准版 2GB
推荐场景:
搭建游戏排行榜
简介: Redis Cluster通过hash slot映射数据,但是如何把两个key映射到同一个slot中呢?

640.png

Redis Cluster集群介绍



你可以这么理解,就是切片集群或者分片集群,用来存储大量数据的。为什么redis要使用它呢?redis的Master-Slave集群不行吗?这个也可以很简单的理解,因为后者是主备存储,前者是集群存储。


主备存储目的就是两个,一个就是防止主从任意一个节点挂掉而导致服务不可用;另一个作用就是缓解读写压力,所有的读取数据的操作不但master可以承担,所有的从节点也可以去承担,这样对于读多写少的场景非常适合,所有的写可以直接写入master节点,然后通过rdb和buffer模式同步给从节点,这样保证整个主备集群数据都是一致的。


但是主备有个缺陷就是无法保存大量数据,因为一旦Master数据超过几十G之后,那么不管是主从集群rdb同步还是命令写入都是非常高危的,严重的情况下会导致主备集群直接不可用,因此为了解决这个问题,redis官方引入了Redis Cluster,它完全可以解决大量数据存储问题。


1. Redis Cluster的目标


  • 分布式的。
  • 高性能和线性扩展,上线可水平扩展到1000个节点。
  • 没有代理,使用异步复制(gossip协议),也没有对值执行合并操作。
  • 那些与大多数节点相连的客户端所做的写入操作,系统尝试全部都保存下来。不过公认的,还是会有小部分写入会丢失。
  • 绝大多数的主节点是可达的,并且对于每一个不可达的主节点都至少有一个它的从节点可达的情况下,Redis 集群仍能进行分区(hash slot)操作。


2. Redis Cluster的命令集


Redis Cluster实现了所有在非分布式Redis版本(单机或者主备)中出现的处理单一键值的命令。

那些使用多个键值的复杂操作,比如set里的并集(unions)和交集(intersections)操作,就没有实现。

Redis Cluster不像单机版本的Redis那样支持多个数据库,集群只有数据库0,而且也不支持SELECT命令。


3. Redis Cluster 通信协议


在 Redis Cluster中,节点负责存储数据、记录集群的状态(包括键值对到正确节点的映射)。集群节点同样能自动发现其他节点,检测出没正常工作的节点,并且在需要的时候在从节点中选出主节点。


为了执行这些任务,所有的集群节点都通过TCP连接和一个二进制协议(集群连接,cluster bus)建立通信。这样每一个节点都通过集群连接(cluster bus)与集群上的其余每个节点连接起来。连接上之后所有节点使用一个gossip协议来传播集群的信息,这样可以:发现新的节点、 发送ping包(用来确保所有节点都在正常工作中)、在特定情况发生时发送集群消息。集群连接也用于在集群中发布或订阅消息。

640.png


由于集群节点不能代理请求,客户端可能被重定向到其他节点使用重定向错误-MOVED和-ASK。从理论上讲,客户端可以自由地向集群中的所有节点发送请求,并在需要时被重定向,因此客户端不需要保存集群的状态。然而,能够缓存键和节点之间的映射的客户端可以提高处理请求性能。


4. Redis Cluster key如何存储


Redis Cluster方案采用哈希槽(Hash Slot)来处理数据和实例之间的映射关系。在Redis Cluster方案中,一个切片集群共有16384个哈希槽,这些哈希槽类似于数据分区,每个键值对都会根据它的key被映射到一个哈希槽中。


具体的映射过程分为两大步:首先根据键值对的key按照CRC16算法计算一个16bit的值;然后再用这个16bit值对16384取模,得到0~16383范围内的模数,每个模数代表一个相应编号的哈希槽。


那么,这些哈希槽又是如何被映射到具体的Redis实例上的呢?


我们在部署Redis Cluster方案时,可以使用cluster create命令创建集群,此时Redis会自动把这些槽平均分布在集群实例上。例如,如果集群中有N个实例,那么每个实例上的槽个数为16384/N个。当然我们也可以使用cluster meet命令手动建立实例间的连接,形成集群,再使用cluster addslots命令,指定每个实例上的哈希槽个数。


一张图来解释一下,数据、哈希槽、实例这三者的映射分布情况:


640.png

图中的切片集群一共有3个实例,同时假设有5个哈希槽,我们首先可以通过下面的命令手动分配哈希槽:实例1保存哈希槽0和1,实例2保存哈希槽2和3,实例3保存哈希槽4。


redis-cli -h 172.16.19.3 –p 6379 cluster addslots 0,1
redis-cli -h 172.16.19.4 –p 6379 cluster addslots 2,3
redis-cli -h 172.16.19.5 –p 6379 cluster addslots 4


在集群运行的过程中,key1和key2计算完CRC16值后,对哈希槽总个数5取模,再根据各自的模数结果,就可以被映射到对应的实例1和实例3上了。另外,在手动分配哈希槽时,需要把16384个槽都分配完,否则Redis集群无法正常工作。


那客户端如何访问呢?


我们上面说过了,Redis Cluster通过重定向错误来处理的。所谓的“重定向”,就是指,客户端给一个实例发送数据读写操作时,这个实例上并没有相应的数据,客户端要再给一个新实例发送操作命令。那客户端又是怎么知道重定向时的新实例的访问地址呢?当客户端把一个键值对的操作请求发给一个实例时,如果这个实例上并没有这个键值对映射的哈希槽,那么,这个实例就会给客户端MOVED命令响应结果,这个结果中就包含了新实例的访问地址。


GET hello:key
(error) MOVED 13320 172.16.19.5:6379

其中,MOVED命令表示,客户端请求的键值对所在的哈希槽13320,实际是在172.16.19.5这个实例上。通过返回的MOVED命令,就相当于把哈希槽所在的新实例的信息告诉给客户端了。这样一来,客户端就可以直接和172.16.19.5连接,并发送操作请求了。


如果这个时候你访问的数正在做迁移,那么就会报错ASK:


GET hello:key
(error) ASK 13320 172.16.19.5:6379


这个结果中的ASK命令就表示,客户端请求的键值对所在的哈希槽13320,在172.16.19.5这个实例上,但是这个哈希槽正在迁移。此时,客户端需要先给172.16.19.5这个实例发送一个ASKING命令。这个命令的意思是,让这个实例允许执行客户端接下来发送的命令。然后,客户端再向这个实例发送GET命令,以读取数据。


5. Redis Cluster hash tag的作用


Redis Cluster在计算hash slot的时候,会同时计算一个特例就是hash tag。这个作用是什么呢?是确保两个键都在同一个哈希槽里。那如何确保这个事情呢?先看一段C源码:


unsigned int HASH_SLOT(char *key, int keylen) {
    int s, e; /* start-end indexes of { and } */
    /* Search the first occurrence of '{'. */
    for (s = 0; s < keylen; s++)
        if (key[s] == '{') break;
    /* No '{' ? Hash the whole key. This is the base case. */
    if (s == keylen) return crc16(key,keylen) & 16383;
    /* '{' found? Check if we have the corresponding '}'. */
    for (e = s+1; e < keylen; e++)
        if (key[e] == '}') break;
    /* No '}' or nothing between {} ? Hash the whole key. */
    if (e == keylen || e == s+1) return crc16(key,keylen) & 16383;
    /* If we are here there is both a { and a } on its right. Hash
     * what is in the middle between { and }. */
    return crc16(key+s+1,e-s-1) & 16383;
}


这段代码不管你会不会C应该都能看懂,非常简单,大家跟着注释把它看完。


大致作用如下:基本来说,如果一个键包含一个“{…}”这样的模式,只有{ 和 }之间的字符串会被用来做哈希以获取哈希槽。但是由于可能出现多个{ 或 },计算的算法如下:


  • 如果键包含一个 { 字符。
  • 那么在 { 的右边就会有一个 }。
  • 在 { 和 } 之间会有一个或多个字符,第一个 } 一定是出现在第一个 { 之后。


只有在第一个{ 和它右边第一个 }之间的内容会被用来计算哈希值,如果中间内容是空,那么整个键会被计算,来看如下例子:


  • 比如这两个键{user1000}.following 和 {user1000}.followers会被哈希到同一个哈希槽里,因为只有user1000这个子串会被用来计算哈希值。
  • 对于foo{}{bar}这个键,整个键都会被用来计算哈希值,因为第一个出现的{ 和它右边第一个出现的 }之间没有任何字符。
  • 对于键foo{{bar}}zap,子字符串{bar将被散列,因为它是第一次出现的{和第一次出现的}在其右侧之间的子字符串。
  • 对于foo{bar}{zap}这个键,用来计算哈希值的是bar这个子串,因为算法匹配到的{}之间有字符的。


按照这个算法,如果一个键是以{}开头的话,那么就当作整个键会被用来计算哈希值。

所以大家想把某个用户的所有相关的键映射到同一个哈希槽里的话,那么你按照hash tag这种方式就可以实现,是不是很简单!

相关实践学习
基于Redis实现在线游戏积分排行榜
本场景将介绍如何基于Redis数据库实现在线游戏中的游戏玩家积分排行榜功能。
云数据库 Redis 版使用教程
云数据库Redis版是兼容Redis协议标准的、提供持久化的内存数据库服务,基于高可靠双机热备架构及可无缝扩展的集群架构,满足高读写性能场景及容量需弹性变配的业务需求。 产品详情:https://www.aliyun.com/product/kvstore &nbsp; &nbsp; ------------------------------------------------------------------------- 阿里云数据库体验:数据库上云实战 开发者云会免费提供一台带自建MySQL的源数据库&nbsp;ECS 实例和一台目标数据库&nbsp;RDS实例。跟着指引,您可以一步步实现将ECS自建数据库迁移到目标数据库RDS。 点击下方链接,领取免费ECS&amp;RDS资源,30分钟完成数据库上云实战!https://developer.aliyun.com/adc/scenario/51eefbd1894e42f6bb9acacadd3f9121?spm=a2c6h.13788135.J_3257954370.9.4ba85f24utseFl
相关文章
|
7天前
|
NoSQL Redis
Redis的数据淘汰策略有哪些 ?
Redis 提供了 8 种数据淘汰策略,分为淘汰易失数据和淘汰全库数据两大类。易失数据淘汰策略包括:volatile-lru、volatile-lfu、volatile-ttl 和 volatile-random;全库数据淘汰策略包括:allkeys-lru、allkeys-lfu 和 allkeys-random。此外,还有 no-eviction 策略,禁止驱逐数据,当内存不足时新写入操作会报错。
39 16
|
12天前
|
消息中间件 缓存 NoSQL
Redis 高并发竞争 key ,如何解决这个难点?
本文主要探讨 Redis 在高并发场景下的并发竞争 Key 问题,以及较为常用的两种解决方案(分布式锁+时间戳、利用消息队列)。关注【mikechen的互联网架构】,10年+BAT架构经验倾囊相授。
Redis 高并发竞争 key ,如何解决这个难点?
|
7天前
|
缓存 NoSQL 关系型数据库
Redis和Mysql如何保证数据⼀致?
在项目中,为了解决Redis与Mysql的数据一致性问题,我们采用了多种策略:对于低一致性要求的数据,不做特别处理;时效性数据通过设置缓存过期时间来减少不一致风险;高一致性但时效性要求不高的数据,利用MQ异步同步确保最终一致性;而对一致性和时效性都有高要求的数据,则采用分布式事务(如Seata TCC模式)来保障。
39 14
|
7天前
|
存储 NoSQL 算法
Redis分片集群中数据是怎么存储和读取的 ?
Redis集群采用哈希槽分区算法,共有16384个哈希槽,每个槽分配到不同的Redis节点上。数据操作时,通过CRC16算法对key计算并取模,确定其所属的槽和对应的节点,从而实现高效的数据存取。
33 13
|
7天前
|
存储 NoSQL Redis
Redis的数据过期策略有哪些 ?
Redis 采用两种过期键删除策略:惰性删除和定期删除。惰性删除在读取键时检查是否过期并删除,对 CPU 友好但可能积压大量过期键。定期删除则定时抽样检查并删除过期键,对内存更友好。默认每秒扫描 10 次,每次检查 20 个键,若超过 25% 过期则继续检查,单次最大执行时间 25ms。两者结合使用以平衡性能和资源占用。
29 11
|
7天前
|
监控 NoSQL 测试技术
【赵渝强老师】Redis的AOF数据持久化
Redis 是内存数据库,提供数据持久化功能,支持 RDB 和 AOF 两种方式。AOF 以日志形式记录每个写操作,支持定期重写以压缩文件。默认情况下,AOF 功能关闭,需在 `redis.conf` 中启用。通过 `info` 命令可监控 AOF 状态。AOF 重写功能可有效控制文件大小,避免性能下降。
|
7天前
|
存储 监控 NoSQL
【赵渝强老师】Redis的RDB数据持久化
Redis 是内存数据库,提供数据持久化功能以防止服务器进程退出导致数据丢失。Redis 支持 RDB 和 AOF 两种持久化方式,其中 RDB 是默认的持久化方式。RDB 通过在指定时间间隔内将内存中的数据快照写入磁盘,确保数据的安全性和恢复能力。RDB 持久化机制包括创建子进程、将数据写入临时文件并替换旧文件等步骤。优点包括适合大规模数据恢复和低数据完整性要求的场景,但也有数据完整性和一致性较低及备份时占用内存的缺点。
|
存储 运维 负载均衡
Redis Cluster集群原理+三主三从交叉复制实战+故障切换
Redis Cluster集群原理+三主三从交叉复制实战+故障切换
1904 0
Redis Cluster集群原理+三主三从交叉复制实战+故障切换
|
3月前
|
存储 NoSQL 算法
深入理解Redis分片Cluster原理
本文深入探讨了Redis Cluster的分片原理,作为Redis官方提供的高可用性和高性能解决方案,Redis Cluster通过数据分片和横向扩展能力,有效降低单个主节点的压力。
深入理解Redis分片Cluster原理
|
6月前
|
运维 NoSQL 算法
Java开发-深入理解Redis Cluster的工作原理
综上所述,Redis Cluster通过数据分片、节点发现、主从复制、数据迁移、故障检测和客户端路由等机制,实现了一个分布式的、高可用的Redis解决方案。它允许数据分布在多个节点上,提供了自动故障转移和读写分离的功能,适用于需要大规模、高性能、高可用性的应用场景。
68 0