三、Redis
1、常见面试题
1.1、认识Redis
Redis 是一种基于内存的数据库,对数据的读写操作都是在内存中完成,因此读写速度非常快,常用于缓存,消息队列、分布式锁等场景。
Redis 提供了多种数据类型来支持不同的业务场景,比如 String(字符串)、Hash(哈希)、 List (列表)、Set(集合)、Zset(有序集合)、Bitmaps(位图)、HyperLogLog(基数统计)、GEO(地理信息)、Stream(流),并且对数据类型的操作都是原子性的,因为执行命令由单线程负责的,不存在并发竞争的问题。
除此之外,Redis 还支持事务 、持久化、Lua 脚本、多种集群方案(主从复制模式、哨兵模式、切片机群模式)、发布/订阅模式,内存淘汰机制、过期删除机制等等。
为什么用 Redis 作为 MySQL 的缓存?
主要是因为 Redis 具备「高性能」和「高并发」两种特性。
1、Redis 具备高性能
假如用户第一次访问 MySQL 中的某些数据。这个过程会比较慢,因为是从硬盘上读取的。将该用户访问的数据缓存在 Redis 中,这样下一次再访问这些数据的时候就可以直接从缓存中获取了,操作 Redis 缓存就是直接操作内存,所以速度相当快。
如果 MySQL 中的对应数据改变的之后,同步改变 Redis 缓存中相应的数据即可,不过这里会有 Redis 和 MySQL 双写一致性的问题,后面我们会提到。
2、 Redis 具备高并发
单台设备的 Redis 的 QPS(Query Per Second,每秒钟处理完请求的次数) 是 MySQL 的 10 倍,Redis 单机的 QPS 能轻松破 10w,而 MySQL 单机的 QPS 很难破 1w。
所以,直接访问 Redis 能够承受的请求是远远大于直接访问 MySQL 的,所以我们可以考虑把数据库中的部分数据转移到缓存中去,这样用户的一部分请求会直接到缓存这里而不用经过数据库。
1.2、Redis 数据结构
Redis 中比较常见的数据类型有下面这些:
- 5 种基础数据类型:String(字符串)、List(列表)、Set(集合)、Hash(散列)、Zset(有序集合)。
- 3 种特殊数据类型:HyperLogLog(基数统计)、Bitmap (位图)、Geospatial (地理位置)。
除了上面提到的之外,还有一些其他的比如 Bloom filter(布隆过滤器)open in new window、Bitfield(位域)。
1.2.1、常用的数据类型
1、string字符串
字符串类型是Redis 最基础的数据结构,首先键是字符串类型,而且其他几种结构都是在字符串类型基础上构建的。字符串类型实际上可以是字符串:简单的字符串、XML、JSON;数字:整数、浮点数;二进制:图片、音频、视频。
使用场景:缓存、计数器、共享Session、限速。
2、Hash(哈希)
在Redis中哈希类型是指键本身是一种键值对结构,如value={field1,value1},…{fieldN,valueN}}
使用场景︰哈希结构相对于字符串序列化缓存信息更加直观,并且在更新操作上更加便捷。所以常常用于用户信息等管理,但是哈希类型和关系型数据库有所不同,哈希类型是稀疏的,而关系型数据库是完全结构化的,关系型数据库可以做复杂的关系查询,而Redis 去模拟关系型复杂查询开发困难且维护成本高。
3、List(列表)
列表类型是用来储存多个有序的字符串,列表中的每个字符串成为元素,一个列表最多可以储存232-1个元素,在Redis 中,可以队列表两端插入和弹出,还可以获取指定范围的元素列表、获取指定索引下的元素等,列表是一种比较灵活的数据结构,它可以充当栈和队列的角色。
使用场景:Redis的lpush + brpop命令组合即可实现阻塞队列,生产者客户端是用lpush 从列表左侧插入元素,多个消费者客户端使用brpop命令阻塞式的“抢"列表尾部的元素,多个客户端保证了消费的负载均衡和高可用性
4、Set(集合)
集合类型也是用来保存多个字符串的元素,但和列表不同的是集合中不允许有重复的元素,并且集合中的元素是无序的,不能通过索引下标获取元素,Redis除了支持集合内的增删改查,同时还支持多个集合取交集、并集、差集。合理的使用好集合类型,能在实际开发中解决很多实际问题。
使用场景:如:一个用户对娱乐、体育比较感兴趣,另一个可能对新闻感兴趣,这些兴趣就是标签,有了这些数据就可以得到同一标签的人,以及用户的共同爱好的标签,这些数据对于用户体验以及曾强用户粘度比较重要。
5、zset (sorted set:有序集合)
有序集合和集合有着必然的联系,它保留了集合不能有重复成员的特性,但不同得是,有序集合中的元素是可以排序的,但是它和列表的使用索引下标作为排序依据不同的是:它给每个元素设置一个分数,作为排序的依据。
使用场景:排行榜是有序集合经典的使用场景。例如:视频网站需要对用户上传的文件做排行榜
榜单维护可能是多方面:按照时
间、按照播放量、按照获得的赞数等。
1.3、AOF和RDB同时开启时的数据恢复流程和加载流程
1.3.1、RDB
RDB(Redis 数据库):RDB 持久性以指定的时间间隔执行数据集的时间点快照。实现类似照片记录效果的方式,就是把某一时刻的数据和状态以文件的形式写到磁盘上,也就是快照。这样一来即使故障宕机,快照文件也不会丢失,数据的可靠性也就得到了保证。这个快照文件就称为RDB文件(dump.rdb),其中,RDB就是Redis DataBase的缩写。
可以用来在指定的时间间隔内将内存中的数据集快照写入磁盘,也就是行话讲的Snapshot内存快照,它恢复时再将硬盘快照文件直接读回到内存里。Redis的数据都在内存中,保存备份时它执行的是全量快照,也就是说,把内存中的所有数据都记录到磁盘中,一锅端。Rdb保存的是dump.rdb文件。
手动触发:
save和bgsave:
Redis提供了两个命令来生成RDB文件,分别是save和bgsave,save在主程序中执行会阻塞当前redis服务器,直到持久化工作完成,执行save命令期间,Redis不能处理其他命令,线上禁止使用。
BGSAVE(默认)
Redis会在后台异步进行快照操作,不阻塞,快照同时还可以响应客户端请求,该触发方式会fork一个子进程由子进程复制持久化过程。Redis会使用bgsave对当前内存中的所有数据做快照,这个操作是子进程在后台完成的,这就允许主进程同时可以修改数据。在Linux程序中,fork()会产生一个和父进程完全相同的子进程,但子进程在此后多会exec系统调用,出于效率考虑,尽量避免膨胀。
适合大规模的数据恢复,按照业务定时备份,对数据完整性和一致性要求不高,RDB文件在内存中的加载速度要比AOF快得多
在一定间隔时间做一次备份,所以如果redis意外down掉的话,就会丢失从当前至最近一次快照期间的数据,快照之间的数据会丢失。内存数据的全量同步,如果数据量太大会导致I/O严重影响服务器性能。RDB依赖于主进程的fork,在更大的数据集中,这可能会导致服务请求的瞬间延迟。fork的时候内存中的数据被克隆了一份,大致2倍的膨胀性,需要考虑。
哪些情况会触发RDB快照
1.配置文件中默认的快照配置手动save/bgsave命令
2.执行flushall/flushdb命令也会产生dump.rdb文件,但里面是空的,无意义
3.执行shutdown且没有设置开启AOF持久化
4.主从复制时,主节点自动触发
1.3.2、AOF
以日志的形式来记录每个写操作,将Redis执行过的所有写指令记录下来(读操作不记录),只许追加文件但不可以改写文件,redis启动之初会读取该文件重新构建数据,换言之,redis重启的话就根据日志文件的内容将写指令从前到后执行一次以完成数据的恢复工作。默认情况下,redis是没有开启AOF(append only file)的。开启AOF功能需要设置配置: appendonly yes。Aof保存的是appendonly.aof文件
AOF持久化流程:
AOF缓冲区三种写回策略:Always(同步写回,每个写命令执行完立刻同步地将日志写回磁盘),everysec(每秒写回,每个写命令执行完,只是先把日志写到AOF文件的内存缓冲区,每隔1秒把缓冲区中的内容写入磁盘),no(操作系统控制的写回,每个写命令执行完,只是先把日志写到AOF文件的内存缓冲区,由操作系统决定何时将缓冲区内容写回磁盘)
优势:更好的保护数据不丢失、性能高、可做紧急恢复
劣势:相同数据集的数据而言aof文件要远大于rdb文件,恢复速度慢于rdb。aof运行效率要慢于rdb,每秒同步策略效率较好,不同步效率和rdb相同
AOF重写机制
自动触发:满足配置文件中的选项后,Redis会记录上次重写时的AOF大小,默认配置是当AOF文件大小是上次rewrite后大小的一倍且文件大于64M时。
手动触发:客户端向服务器发送bgrewriteaof命令
启动AOF文件的内容压缩,只保留可以恢复数据的最小指令集。
举个例子:比如有个key
一开始你 set k1 v1
然后改成 set k1 v2
最后改成 set k1 v3
如果不重写,那么这3条语句都在aof文件中,内容占空间不说启动的时候都要执行一遍,共计3条命令;但是,我们实际效果只需要set k1 v3这一条,所以,开启重写后,只需要保存set k1 v3就可以了只需要保留最后一次修改值,相当于给aof文件瘦身减肥,性能更好。AOF重写不仅降低了文件的占用空间,同时更小的AOF也可以更快地被Redis加载。
重写原理:1:在重写开始前,redis会创建一个“重写子进程”,这个子进程会读取现有的AOF文件,并将其包含的指令进行分析压缩并写入到一个临时文件中。
2:与此同时,主进程会将新接收到的写指令一边累积到内存缓冲区中,一边继续写入到原有的AOF文件中,这样做是保证原有的AOF文件的可用性,避免在重写过程中出现意外。
3:当“重写子进程”完成重写工作后,它会给父进程发一个信号,父进程收到信号后就会将内存中缓存的写指令追加到新AOF文件中
4:当追加结束后,redis就会用新AOF文件来代替旧AOF文件,之后再有新的写指令,就都会追加到新的AOF文件中
5:重写aof文件的操作,并没有读取旧的aof文件,而是将整个内存中的数据库内容用命令的方式重写了一个新的aof文件,这点和快照有点类似
1.3.3、RDB-AOF混合持久化
1.RDB持久化方式能够在指定的时间间隔能对你的数据进行快照存储
2.AOF持久化方式记录每次对服务器写的操作,当服务器重启的时候会重新执行这些命令来恢复原始的数据,AOF命令以redis协议追加保存每次写的操作到文件末尾.。
同时开启两种持久化方式:
在这种情况下,当redis重启的时候会优先载入AOF文件来恢复原始的数据,因为在通常情况下AOF文件保存的数据集要比RDB文件保存的数据集要完整,(因为RDB快照生成是看自己设置的,可能是15分钟,那么就可能丢失15分钟内做的修改,而AOF是秒级别的)
RDB的数据不实时,同时使用两者时服务器重启也只会找AOF文件。那要不要只使用AOF呢?
不要!因为RDB更适合用于备份数据库(AOF在不断变化不好备份),留着RDB作为一个万一的手段。
1.4、Redis主从复制
1.4.1、简介
一句话就是master以写为主,Slave以读为主,当master数据变化时,自动将新的数据异步同步到其它slave数据库
它主要实现了下面这些特性:
-
读写分类:写在master写,读在slave读
-
容灾恢复
-
数据备份
-
水平扩容,支持高并发
master如果配置了requirepas
参数,需要密码登陆,那么slave就要配置masterauth
来设置校验密码,否则的话master会拒绝slave的请求。
1.4.2、一主二从
- 从机只能读数据而不能写数据
- 启动的晚的从机,也会同步到该从机启动之前主机操作的数据,然后开始跟随
- 主机宕机后,从机并不会成为主机,从机数据可以正常使用,它会一直等待主机重启
1.4.3、薪火相传
1、上—个slave可以是下一个slave的master,slave同样可以接收其他slaves的连接和同步请求,那么该slave作为了链条中下一个的master,可以有效减轻主master的写压力(该节点也可以帮助master对其他slave完成完成同步,但该节点还是没有set写能力)
2、中途变更转向:会清除之前的数据,重新建立拷贝最新的
3、slaveof 新主库IP 新主库端口
1.4.4、反客为主:
SLAVEOF no one
使当前数据库停止与其他数据库的同步,转成主数据库
1.4.5、复制原理和工作流程
1、slave启动,同步初请:
slave启动成功连接到master后会发送一个sync命令(同步命令)
slave首次全新连接master,一次完全同步(全量复制)将被自动执行,slave自身原有数据会被master数据覆盖清除
2、首次连接,全量复制
master节点收到sync命令后会开始在后台保存快照(即RDB持久化,主从复制时会触发RDB),同时收集所有接收到的用于修改数据集命令缓存起来,master节点执行RDB持久化完后,master将rdb快照文件和所有缓存的命令发送到所有slave,以完成一次完全同步。
而slave服务在接收到数据库文件数据后,将其存盘并加载到内存中,从而完成复制初始化。
3、心跳持续,保持通信
repl-ping-replica-period 10
4、进入平稳,增量复制
Master继续将新的所有收集到的修改命令自动依次传给slave,完成同步
5、从机下线,重连续传
master会检查backlog里面的offset,master和slave都会保存一 个 复制的offset还有一个masterId,
offset是保存在backlog中的。Master只 会把已经复制的offset后面的数据复制给Slave,类似断点续传
1.4.5、主从复制的缺点
复制延时,信号哀减
由于所有的写操作都是先在Master上操作,然后同步更新到Slave上,所以从Master同步到Slave机器有一定的延迟,当系统很繁忙的时候,延迟问题会更加严重,Slave机器数量的增加也会使这个问题更加严重。
master挂了如何办?
默认情况下,不会在slave节点中自动重选一个master。那每次都要人工干预?无人值守安装变成刚需
1.5、Redis哨兵(Sentinel)
1.5.1、什么是Redis哨兵
Redis哨兵(Sentinel)是Redis官方推荐的高可用解决方案。在Redis的主从复制模式中,我们有一个主Redis服务,和多个从Redis服务。但是,如果此时主Redis服务挂掉了,那么系统就会自动通过Sentinel选举出一个新的主Redis服务,其他的从Redis服务会自动连接新的主Redis服务。这就保证了系统的高可用。
以下是Redis哨充的主要功能:
1、监控工作:哨兵通过发送命令,来监控主服务器和从服务器是否正常运行。
2、提供通知:当被监控的某个Redis实例有故障时,Sentinel可以通过API向管理员或者其他应用程序发送通知。
3、自动故障迁移:当一个主节点不能正常工作时,Sentinel会开始一次故障迁移操作,它会选举出一个从节点来作为新的主节点,并让其他从节点复制新的主节点。
4、配置提供者:客户端在连接Redis集群时,可以先连接到Sentinel查询主节点和从节点的信息,然后再决定连接到哪个节点。
比如,一个用Redis作为缓存的Web应用,如果使用了Sentinel,那么当Redis主节点由于某些原因宕机后,可以自动进行故障切换,选择一个从节点提升为主节点,以此来保证服务的持续可用,防止由于Redis节点的宕机导致应用无法访问缓存而出现的性能瓶颈。
1.5.2、哨兵选举流程
Redis Sentinel在主节点故障时会选择一个从节点晋升为新的主节点,过程如下:
1、故障检测: Sentinel会不断地通过心跳检测Redis节点的健康状态。当主节点不可达时,检测到主节点down掉,这个Sentinel会等待指定时间(例如10秒)后,开始下一步。
即检测master是不是SDOWN(主观不可用),SDOWN是单个sentinel自己主观上检测到的关于master的状态,从Sentinel的角度来说,如果发送了ping心跳后,在一定时间内没有收到合法的恢复,就达到了SDOWN的条件。
Sentinel的配置文件中的down-after-milliseconds设置了判断主观下线的时间
⒉、发起投票:该Sentinel会向其他Sentinels发送一个故障转移的请求,请求得到的多数同意(超过半数)就会开始主节点的故障转移。
ODOWN(客观不可用)
ODOWN需要一定数量的sentinel,多个哨兵达成一致意见后才能认为一个master客观上宕机了
3、选举出领导者哨兵(哨兵中选出兵王)
当主节点被判断客观下线以后,各个哨兵节点会进行协商,先选举出一个领导者哨兵节点(兵王)并由该领导者节点,也即被选举出的兵王进行failover(故障迁移),通过Raft算法选举
4、选择新的主节点: Sentinel会选择一个从节点来进行晋升。选择策略主要考虑以下因素:复制偏移量最大的节点〈即数据最新的节点)、运行ID较小的节点(这是为了在其他因素都相等的情况下,每个Sentinel都能选出相同的节点)、没有被其他Sentinel标记为主观下线的节点。先按照第一规则选举,如果都相等按照第二规则,依此类推,最后能找出一个从节点。
5、晋升选举出的从节点为主节点:Sentinel向选举出的从节点发送命令,关闭其对旧的主节点的同步,让其成为新的主节点。(执行slaveof no one命令让选出来的从节点成为新的主节点),通知其他从节点、客户端和Sentinels更改主节点:晋升成功后,该Sentinel会通知其他的从节点、客户端和Sentinel更改主节点。
6、将之前已经下线的老master设置为新选出的master的从节点,当老master重新上线后,它会成为新master的从节点。
注意:整个故障转移过程中,可能会有多个Sentinel发起投票,但只有先获得大多数Sentinel认可的才能开始故障转移,且在故障转移过程中,其他Sentinel不会再发起新的投票。
1.5.3、哨兵使用建议
1、哨兵节点的数量应为多个,哨兵本身应该集群,保证高可用
2、哨兵节点的数量应该是奇数
3、各个哨兵节点的配置应一致
4、如果哨兵节点部署在Docker等容器里面,尤其要注意端口的正确映射
5、哨兵集群+主从复制,并不能保证数据零丢失。所以要引入集群
1.6、Redis集群
1.6.1、什么是集群
由于数据量过大,当个master复制集难以承担,因此需要多个复制集进行集群,形成水平扩展每个复制集只负责存储整个数据集的一部分,这就是Redis集群,其作用时在多个Redis节点间共享数据的程序集。
-
Redis集群支持多个Master,每个Master又可以挂载多个slave(读写分离、支持数据的高可用、支持海量数据的读写存储操作)
-
由于集群自带Sentinel的故障转移机制,内置了高可用的支持,无需再去使用哨兵功能
-
客户端于Redis的节点连接,不再需要连接集群中所有节点只需要任意连接集群中的一个可用节点即可
-
槽位slot负责分配各个物理服务节点,由对应的就去那来负责维护节点、插槽和数据之间的关系
1.6.2、集群算法-分片-槽位slot
Redis集群没有使用一致性Hash,而是引入了哈希槽的概念。Redis集群最多有16384个哈希槽,官网建议集群哈希槽设置在1000个以内。
每个key通过CRC16校验后对16384取模来决定放置在哪个槽,集群的每一个节点负责一部分hash槽。如下:
分片是什么:使用Redis集群时我们会将存储的数据分散到多台redis机器上,这称为分片。简言之,集群中的每个Redis实例都被认为是整个数据的一个分片。
如何找到给定key:为了找到给定key的分片,我们对key进行CRC16(key)算法处理并通过对总分片数量取模。然后,使用确定性哈希函数,这意味着给定的key将多次始终映射到同一个分片,我们可以推断将来读取特定key的位置。
优势:方便扩缩容和数据分配查找,这种结构很容易添加或者删除节点.比如观果我想新添加个节点D,我需要从节点A,B,C中得部分槽到D上.如果我想移除节点A需要将A中的槽移到B和C节点上,然后将没有任何槽的A节点从集群中移除即可.由于从一个节点将哈希槽移动到另一个节点并不会停止服务,所以无论添加删除或者改变某个节点的哈希槽的数量都不会造成集群不可用的状态.
slot槽位映射,一般业界有3种解决方案
1、哈希取余分区:
2亿条记录就是2亿个k,v,我们单机不行必须要分布式多机,假设有3台机器构成一个集群,用户每次读写操作都是根据公式:hash(key) % N个机器台数,计算出哈希值,用来决定数据映射到哪一个节点上。
优点:
简单粗暴,直接有效,只需要预估好数据规划好节点,例如3台、8台、10台,就能保证一段时间的数据支撑。使用Hash算法让固定的一部分请求落到同一台服务器上,这样每台服务器固定处理一部分请求(并维护这些请求的信息),起到负载均衡+分而治之的作用。
缺点:
原来规划好的节点,进行扩容或者缩容就比较麻烦了额,不管扩缩,每次数据变动导致节点有变动,映射关系需要重新进行计算,在服务器个数固定不变时没有问题,如果需要弹性扩容或故障停机的情况下,原来的取模公式就会发生变化:Hash(key)/3会变成Hash(key)/?。此时地址经过取余运算的结果将发生很大变化,根据公式获取的服务器也会变得不可控。某个redis机器宕机了,由于台数数量变化,会导致hash取余全部数据重新洗牌。
2、一致性哈希算法分区
3大步骤:
1)算法构建一致性哈希环
一致性哈希算法必然有个hash函数并按照算法产生hash值,这个算法的所有可能哈希值会构成一个全量集,这个集合可以成为一个hash空间[0,2 ^ 32-1 ],这个是一个线性空间,但是在算法中,我们通过适当的逻辑控制将它首尾相连(0 = 2^32),这样让它逻辑上形成了一个环形空间。
它也是按照使用取模的方法,前面笔记介绍的节点取模法是对节点(服务器)的数量进行取模。而一致性Hash算法是对2 ^ 32取模,简单来说,一致性Hash算法将整个哈希值空间组织成一个虚拟的圆环,如假设某哈希函数H的值空间为0-2 ^ 32-1(即哈希值是一个32位无符号整形),整个哈希环如下图:整个空间按顺时针方向组织,圆环的正上方的点代表0,0点右侧的第一个点代表1,以此类推,2、3、4、……直到2 ^ 32-1,也就是说0点左侧的第一个点代表2 ^ 32-1, 0和2 ^ 32-1在零点中方向重合,我们把这个由2^32个点组成的圆环称为Hash环。
2)Redis服务器IP节点映射
将集群中各个IP节点映射到环上的某一个位置。将各个服务器使用Hash进行一个哈希,具体可以选择服务器的IP或主机名作为关键字进行哈希,这样每台机器就能确定其在哈希环上的位置。假如4个节点NodeA、B、C、D,经过IP地址的哈希函数计算(hash(ip)),使用IP地址哈希后在环空间的位置如下:
-
key落到服务器的落键规则
当我们需要存储一个k,v键值对时,首先计算key的hash值,hash(key),将这个key使用相同的函数Hash计算出哈希值并确定此数据在环上的位置,从此位置沿环顺时针“行走”,第一台遇到的服务器就是其应该定位到的服务器,并将该键值对存储在该节点上。如我们有Object A、Object B、Object C、Object D四个数据对象,经过哈希计算后,在环空间上的位置如下:根据一致性Hash算法,数据A会被定为到Node A上,B被定为到Node B上,C被定为到Node C上,D被定为到Node D上
该算法具有容错性。假设Node C宕机了,可以看到此时A,B,D不会受到影响,一般的在一致性Hash算法中,如果一台服务器不可用了,则受影响的仅仅是此服务器到其环空间前的一台服务器之间的数据,其它不会受到影响。
该算法具有扩展性。数据量增加了,需要增加一台NodeX,X的位置在A和B之间,那受到影响的是A到X之间的数据,重新把A到X的数据录入到X上即可,不会导致hash取余全部数据重新洗牌。
该算法的缺点是存在Hash环的数据倾斜问题。一致性hash算法在服务节点太少时,容易业务节点分布不均匀导致数据倾斜(被缓冲的对象大部分集中缓存在一台服务器上)问题。
3、哈希槽分区
该算法的出现是为了解决一致性hash算法的倾斜问题。hash槽本质上是一个数组,数组[0,214]形成的hash slot空间。它在数据和节点之间加入了一层,这层就称为槽,用于管理数据和节点之间的关系,现在就相当于节点上放的是槽,槽中放的是数据。
为什么redis集群的最大槽数为16384个?
答:CRC16算法产生的hash值有16bit,可以产生65536个值,为什么只用16384就够了(214)?这是因为如果槽位为65536,发送心跳信息的消息头达8K,发生的心跳包过于庞大。在消息头中最占空间的是myslots[CLUSTER_SLOTS/8],当槽位为65536时,这块的大小为65536/8/1024=8kb,当槽位为16384时,这块的大小为2kb,因为每秒钟,redis节点需要发送一定数量的ping消息作为心跳包,如果槽位为65536,这个ping的消息头就太大了,浪费带宽。其次,集群节点越多,心跳包的消息体携带的数据越多,如果节点超过1000个,也会导致网络赌赛,因此redis作者不建议redis cluster节点数量超过1000个,那么,对于节点数在1000以内的redis cluster,16384个槽位够用了,没有必要扩展到65536个槽。最后,槽位越小,节点少的情况下,压缩比高,容易传输。Redis主节点的配置信息中它负责的哈希槽是通过一张bitmap形式来保存的,在传输过程中会对bitmap进行压缩,如果bitmap的填充率slots/N很高的话(N表示节点数),bitmap的压缩率就比较低,如果节点数很少,而hash槽数量多的话,bitmap的压缩率就很高。
1.6.3、总结Redis集群模式是如何工作的?数据如何分片?
Redis集群模式是一种分布式数据库解决方案,它允许将数据分散到多个Redis实例上,以实现高可用性、可扩展性和容错性。Redis集群模式通过分片(sharding)将数据分布到不同的节点上,每个节点都负责存储一部分数据。
在Redis集群模式中,数据被划分为多个槽(slot),每个槽负责存储一部分键值对。默认情况下,Redis集群包含16384个槽,槽是数据分片的基本单位。每个Redis节点都被分配了一定数量的槽,并且每个节点只处理自己负责的槽相关的命令请求。这种设计使得Redis集群可以水平扩展,通过增加节点来扩展存储容量和性能。
Redis集群使用一种称为哈希槽(hash slot)的算法来确定键应该存储在哪个节点上。该算法使用CRC16校验和对键进行哈希计算,然后将结果对16384取模,得到一个介于0和16383之间的值作为哈希槽。Redis集群中的每个节点都负责处理一部分哈希槽。当客户端向集群发送命令时,集群会根据键计算出对应的哈希槽,然后将命令路由到负责该哈希槽的节点上执行。
Redis集群模式还支持节点的自动故障转移和恢复。当一个节点出现故障时,集群中的其他节点会自动检测到该节点的不可用状态,并将该节点的槽重新分配给其他可用的节点。这个过程是透明的,对于客户端来说,不需要做任何额外的操作。当故障节点恢复后,集群会自动将数据重新平衡到原来的节点上。
总之,Redis集群模式通过分片、哈希槽和自动故障转移等技术,实现了高可用、可扩展和容错的分布式数据库解决方案。
1.6.4、谈谈你在Redis集群中遇到过的难题
我相信贵公司用的应该也是经典的三主三从Redis集群。我以前突然碰到的问题就是,有一次我们 Redis主节点宕机(6381端口)了,当然我们知道这时候Redis集群会进行补救,让从节点(6384端口)晋升为主节点,那我们就会认为Redis端被及时补救了,后端的命令也能够正确发送应答。但我发现后端发送相同的指令,它还是会先去原本的6381端口中找,而不会去找它的从机,从而导致连接超时,在用户看来就是网络卡顿。这就导致了Redis的高可用性急剧下降。
这就是redis集群的经典故障:SpringBoot客户端没有动态感知到RedisCluster的最新集群消息
解救方法:刷新节点集群拓扑动态感应
spring:
redis:
database: 0 # 数据库,每个库由16384(0~16383)槽位组成,实现了库的分片
password: xxx@xxx# 密码
timeout: 50000 # 连接超时时间
cluster:
nodes:
- 192.168.4.26:6379
- 192.168.4.26:6380
- 192.168.4.26:6481
- 192.168.4.26:6482
- 192.168.4.26:6583
- 192.168.4.26:6584
max-redirects: 3 # 获取失败 最大重定向次数
lettuce:
pool:
max-idle: 10
max-active: 10
min-idle: 0
#重点在这! 配置refresh,支持集群拓扑动态感应刷新
cluster:
refresh:true # 动态感应
adaptive: true
#定时刷新
period: 10000