Redis高级部分(集群,发布订阅)
Redis高级部分:集群、发布订阅机制详解
Redis 主从复制
1. 主从复制概念
主从复制架构主要用于数据的冗余备份,从节点仅用于同步数据。它无法自动处理主节点故障转移。
2. 主从复制架构图

3. 搭建主从复制步骤
3.1 准备环境

3.2 配置文件
拷贝源码中的redis.conf到每个目录,并修改配置以设置端口号、开启远程连接和配置主节点。
-
master:
- 将
127.0.0.1注释 port 8000protected-mode no
![image-20241028221105756]()
![image-20241028221215059]()
![image-20241028221306153]()
- 将
-
node1:
port 8001protected-mode noreplicaof <masterip> <masterport>(例如:replicaof 192.168.40.110 8000)
-
node2:
port 8002protected-mode noreplicaof <masterip> <masterport>(例如:replicaof 192.168.40.110 8000)

3.3 启动测试
启动主节点7000服务(命令中多打了一个r)

新建终端启动7001(7002同样启动)

主从同步成功




3.4DataGrip创建连接(从节点一样注意端口号)



Redis哨兵机制
1. 哨兵Sentinel机制
Sentinel(哨兵)是Redis 的高可用性解决方案:由一个或多个Sentinel 实例组成的Sentinel 系统可以监视任意多个主服务器,以及这些主服务器属下的所有从服务器,并在被监视的主服务器进入下线状态时,自动将下线主服务器属下的某个从服务器升级为新的主服务器。简单的说哨兵就是带有自动故障转移功能的主从架构。
无法解决: 1.单节点并发压力问题 2.单节点内存和磁盘物理上限
2. 哨兵架构原理

Redis集群
1. 集群概念
Redis 3.0后支持Cluster模式,支持节点自动发现、slave-master选举、容错和在线分片等特性。
2. 集群架构图

3. 集群细节
- 所有Redis节点互联,使用PING-PONG机制。
- 节点失效由超过半数节点检测确认。
- 客户端直连节点,不需要proxy层。
4. 集群搭建
搭建集群至少需要三个主节点和三个从节点,共六个节点。
4.1 环境准备
安装Ruby和Redis集群依赖。
# 1.准备环境安装ruby以及redis集群依赖
- yum install -y ruby rubygems
# https://rubygems.org/gems/redis/versions
- gem install redis-xxx.gem

4.2 配置文件
在一台机器上创建七个目录,每个目录复制一份配置文件,并修改端口、开启远程连接、设置集群模式等。
在bigdata32目录下创建redis-jiqun目录,进入redis-jiqun目录下创建7000到7006文件

使用命令将配置文件复制到对应文件里
cp /usr/local/soft/jars/redis-7.0.0/redis.conf ./7000/
......
修改以下配置文件
# 4.修改不同目录配置文件
- port 7000 ..... //修改端口
- # bind 127.0.0.1 -::1 //开启远程连接
- protected-mode no
- daemonize yes //开启守护进程
- dbfilename dump-7000.rdb //每台机器的文件不能一样
- cluster-enabled yes //开启集群模式
- cluster-config-file nodes-7000.conf //集群节点配置文件
- cluster-node-timeout 10000 //集群节点超时时间
- appendonly yes //开启AOF持久化
- appendfilename "appendonly-7000.aof" //修改aof文件名
- appenddirname "appendonlydir-7000" //redis7.0 以上的版本需要配置
可以将7000的配置文件修改好之后复制给其他的配置文件,其他的修改时只需要把端口号7000改为对应的端口号即可







4.3 启动节点
启动七个redis服务
redis-server 7000/redis.conf
.......

创建集群
#1.复制集群操作脚本到bin目录中(这里的路径是自己解压文件的路径redis-7.0.0/src/redis-trib.rb)
cp /usr/local/soft/jars/redis-7.0.0/src/redis-trib.rb /usr/local/soft/redis/bin/
#2.7000到7005创建集群
redis-cli --cluster create 192.168.118.101:7000 192.168.118.101:7001 192.168.118.101:7002 192.168.118.101:7003 192.168.118.101:7004 192.168.118.101:7005 --cluster-replicas 1

这里会自动分配主节点和从节点,需要输入yes,表示同意

将节点加入集群的操作

查看集群状态
# 1.查看集群状态 check [原始集群中任意节点] [无]
redis-cli --cluster check 192.168.118.101:7000
# 2.集群节点状态说明
- 主节点
主节点存在hash slots,且主节点的hash slots 没有交叉
主节点不能删除
一个主节点可以有多个从节点
主节点宕机时多个副本之间自动选举主节点
- 从节点
从节点没有hash slots
从节点可以删除
从节点不负责数据的写,只负责数据的同步
Redis面试题与理解:穿透、雪崩、击穿
# 1、缓存穿透:(要查询的数据根本不存在)
是指用户查询数据,在数据库没有,自然在缓存中也不会有。这样就导致用户查询的时候,在缓存中找不到,每次都要去数据库再查询一遍,然后返回空(相当于进行了两次无用的查询)。
这样请求就绕过缓存直接查数据库,这也是经常提的缓存命中率问题。 当请求查询一个不存在于缓存中,也不存在于后端数据源中的数据时,每次请求都会直接访问后端数据源。
这可能导致后端系统负载增加,甚至引起拒绝服务攻击。
# 2、缓存雪崩:(一批数据有,但是过期时间到了)
简单的理解为:由于原有缓存失效,新缓存未到时间 (例如:设置缓存时采用了相同的过期时间,在同一时刻出现大面积的缓存过期),所有原本应该访问缓存的请求都去查询数据库了,
而对数据库CPU和内存造成巨大压力,严重的会造成数据库宕机。从而形成一系列连锁反应,造成整个系统崩溃。指的是当针对一个热点数据的并发请求同时失效,
导致大量请求落到后端数据源上,压垮后端数据库或造成服务不可用。
# 3、缓存击穿:(针对某一个数据突然过期,直接查数据库)
某个 key 非常非常热,访问非常的频繁,高并发访问的情况下,当这个 key在失效(可能expire过期了,也可能LRU淘汰了)的瞬间,大量的请求进来,这时候就击穿了缓存,
直接请求到了数据库,一下子来这么多,数据库肯定受不了,这就叫缓存击穿。某个key突然失效,然后这时候高并发来访问这个key,结果缓存里没有,都跑到db了。和缓存雪崩不同的是,
缓存击穿指并发查同一条数据,缓存雪崩是不同数据都过期了,很多数据都查不到从而查数据库。缓存雪崩指的是当缓存中大量的数据同时过期,导致大量请求落到后端数据源上,
造成数据库压力过大甚至服务不可用。
# 三者出现的根本原因是:Redis缓存命中率下降,请求直接打到DB上了
解决方案:
1. 缓存穿透
根本原因(结合上文)就是:请求根本不存在的资源
举例:客户端发送大量的不可响应的请求(如下图)

当大量的客户端发出类似于:https://localhost:8080/users?id=-1234 的请求,就可能导致出现缓存穿透的情况。因为数据库DB中本身就没有id=-1234的用户的数据,所以Redis也没有对应的数据,那么这些请求在redis就得不到响应,就会直接打在DB上,导致DB压力过大而卡死情景在线或宕机
解决方案:
# 对空值进行缓存
类似于上面的例子,虽然数据库中没有id=-1234的用户的数据,但是在redis中对他进行缓存(key=-1234,value=null),这样当请求到达redis的时候就会直接返回一个null的值给客户端,避免了大量无法访问的数据直接打在DB上。
# 实时监控
对redis进行实时监控,当发现redis中的命中率下降的时候进行原因的排查,配合运维人员对访问对象和访问数据进行分析查询,从而进行黑名单的设置限制服务。
# 使用Boolean过滤器
使用BitMap作为布隆过滤器,将目前所有可以访问到的资源通过简单的映射关系放入到布隆过滤器中(哈希计算),当一个请求来临的时候先进行布隆过滤器的判断,如果有那么才进行放行,否则就直接拦截。
# 接口校验
类似于用户权限的拦截,对于id=-1234这些无效访问就直接拦截,不允许这些请求到达Redis、DB上。
2. 缓存雪崩
产生的原因:redis中大量的key集体过期
比如:当redis中的大量key集体过期,可以理解为redis中的大部分数据都被清空了(失效了),那么这时候如果有大量并发的请求来到,那么redis就无法进行有效的响应(命中率急剧下降),请求就都打到DB上了,到时DB直接崩溃。
解决方案:
1. 使用互斥锁(Mutex Lock)或分布式锁,只允许一个请求去访问后端数据源,其他请求等待并共享结果。
2. 将失效时间分散开, 通过使用自动生成随机数使得key的过期时间是随机的,防止集体过期
3. 使用多级架构,使用nginx缓存+redis缓存+其他缓存,不同层使用不同的缓存,可靠性更强
4. 设置缓存标记,记录缓存数据是否过期,如果过期会触发通知另外的线程在后台去更新实际的key
5. 设置热点数据的永远不过期或过期时间较长,以减少热点数据失效的机会。
3. 缓存击穿
产生的原因:redis中的某个热点key过期,但是此时有大量的用户访问该过期key。
比如:类似于“刘某某今日结婚”上了热搜,这时候大量的“粉丝”都在访问该热点事件,但是可能由于某种原因,redis的这个热点key过期了,那么这时候大量高并发对于该key的请求就得不到redis的响应,那么就会将请求直接打在DB服务器上,导致整个DB瘫痪。
解决方案:
1. 为缓存数据设置不同的过期时间,使其在不同时间点过期,避免集中失效。监控数据,适时调整,监控哪些数据是热门数据,实时的调整key的过期时长
2. 引入两级缓存架构,例如使用本地缓存(如Guava Cache)作为第一级缓存,并设置较短的过期时间,Redis作为第二级缓存,并设置较长的过期时间。
3. 针对热点数据,可以提前进行预加载,保证其缓存不会在同一时间全部失效。
Redis发布与订阅模式详解:基于频道和基于模式的订阅
概述
Redis的发布/订阅模式允许发布者向频道发送消息,而订阅者可以订阅这些频道以接收消息。这种模式支持两种类型的订阅:基于频道的订阅和基于模式的订阅。
基于频道的订阅
基于频道的订阅是最直接的发布/订阅模式,订阅者订阅一个或多个频道,当发布者向这些频道发送消息时,所有订阅者都会收到消息。
命令
SUBSCRIBE channel1 [channel2 ...]:订阅一个或多个频道。UNSUBSCRIBE channel1 [channel2 ...]:退订一个或多个频道。PUBLISH channel message:向频道发布消息。
代码示例
发布者
import redis.clients.jedis.Jedis;
public class ChannelPublisher {
public static void main(String[] args) {
Jedis jedis = new Jedis("localhost", 6379);
String channel = "testChannel";
String message = "Hello, Redis Channel!";
jedis.publish(channel, message);
System.out.println("Published to channel: " + message);
jedis.close();
}
}
订阅者
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPubSub;
public class ChannelSubscriber {
public static void main(String[] args) {
Jedis jedis = new Jedis("localhost", 6379);
jedis.subscribe(new JedisPubSub() {
@Override
public void onMessage(String channel, String message) {
System.out.println("Received message from channel " + channel + ": " + message);
}
}, "testChannel");
}
}
基于模式的订阅
基于模式的订阅允许订阅者订阅符合特定模式的频道。这在需要订阅多个频道,而这些频道具有共同前缀或模式时非常有用。
命令
PSUBSCRIBE pattern1 [pattern2 ...]:订阅一个或多个符合给定模式的频道。PUNSUBSCRIBE pattern1 [pattern2 ...]:退订一个或多个符合给定模式的频道。
代码示例
模式订阅者
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPubSub;
public class PatternSubscriber {
public static void main(String[] args) {
Jedis jedis = new Jedis("localhost", 6379);
jedis.psubscribe(new JedisPubSub() {
@Override
public void onPMessage(String pattern, String channel, String message) {
System.out.println("Received message from pattern " + pattern + ", channel " + channel + ": " + message);
}
}, "test*");
}
}
在这个例子中,订阅者订阅了所有以“test”开头的频道。
注意事项
-
消息可靠性:Redis的发布/订阅模式不保证消息的可靠传递。如果订阅者在消息发布之前已断开连接,则无法接收到已发布的消息。
-
消息顺序:Redis不保证消息的顺序性,消息可能会乱序到达。
-
性能考虑:在高吞吐量和大规模发布订阅场景下,Redis的发布/订阅模式可能不是最佳选择,可以考虑使用专门的消息队列系统来替代。
-
客户端连接:订阅者一旦使用
SUBSCRIBE或PSUBSCRIBE命令后,将会一直等待新消息。客户端可以使用UNSUBSCRIBE或PUNSUBSCRIBE命令取消订阅。
通过上述详细解释和代码示例,你应该能够更好地理解和实现Redis的发布/订阅模式,包括基于频道的订阅和基于模式的订阅。这些知识可以帮助你根据实际应用场景选择合适的订阅方式。




浙公网安备 33010602011771号