黑马点评
分布式缓存
Redis集群
Redis持久化(RDB和AOF)
配置的redis文件位置
/usr/local/src/redis-6.2.6/redis.conf
默认停机的时候执行一次rdb持久化方案
知识点(RDB、AOF、两者混合的工业应用)
-
fork 主进程就是指:主进程(父进程)通过调用 fork() 系统调用,创建一个与其几乎完全一样的副本,即“子进程”。fork过程中主进程阻塞。
-
1. RDB (Redis Database)
RDB 主要有两种触发方式,它们的同步/异步表现完全不同:
-
SAVE命令:同步(阻塞)这是同步执行的。Redis 主进程会停止处理任何客户端命令,直到 RDB 文件生成完毕。这在生产环境中是非常危险的,因为它会导致服务瞬间“卡死”。
-
BGSAVE命令:异步(非阻塞)这是 Redis 默认和常用的方式。
- 原理:主进程会
fork()出一个子进程。 - 表现:由子进程负责将内存数据写入磁盘生成 RDB 文件,而主进程继续处理客户端的读写请求。
- 注意:虽然写入磁盘是异步的,但
fork()那个瞬间是同步的,如果内存特别大,可能会有毫秒级的卡顿。
- 原理:主进程会
2. AOF (Append Only File)
AOF 的“异步性”取决于
appendfsync参数的设置,它决定了数据从 内存缓冲区 刷到 磁盘文件 的频率:配置项 是否异步 说明 always同步 每执行一个写命令就执行一次 fsync刷盘。最安全但最慢,主线程会被阻塞直到磁盘写入完成。everysec异步 (默认) 每秒执行一次刷盘。Redis 会创建一个 后台线程 专门负责 fsync,主线程只管写内存缓冲区,所以是异步的。no由系统决定 Redis 只负责把数据写进操作系统的缓冲区,什么时候真写到磁盘由操作系统(OS)说了算(通常 30 秒)。 -
-
在工业界,这种结合不仅存在,而且已经是 Redis 4.0 版本之后的标准配置。它被称为 “混合持久化” (Hybrid Persistence)。
以前,开发者常陷入两难:
- 只用 RDB:恢复快,但两次快照间会丢失较多数据。
- 只用 AOF:数据安全,但日志文件巨大,重启恢复慢得要命(需要重放成千上万条命令)。
1. 它是如何结合的?
在 Redis 4.0 及以上版本,你可以通过配置开启混合持久化:
aof-use-rdb-preamble yes它的工作流程如下:
- 定期“拍快照”:当 AOF 文件触发重写(Rewrite)时,Redis 不再单纯地把内存命令转成文本,而是先将当前的内存数据以 RDB 的二进制格式 写入 AOF 文件的开头。
- 增量“记日志”:在重写期间发生的新的写命令,依然以 AOF 文本格式 追加到文件的末尾。
2. 文件的结构
最终生成的
appendonly.aof文件长这样:- [前半部分]:二进制内容(RDB 格式,代表某个时间点的全量数据)。
- [后半部分]:文本命令(AOF 格式,代表快照之后的增量操作)。
3. 混合持久化的优势
这是目前工业界(如阿里云、腾讯云的 Redis 服务)的首选方案:
- 重启速度极快:加载前半部分的 RDB 就像直接拷贝内存镜像,比逐条执行 AOF 命令快得多。
- 安全性高:后半部分的 AOF 保证了即使在两次快照之间宕机,也只丢失极少(通常 1 秒内)的数据。
- 文件更小:二进制的 RDB 头部比等效的 AOF 命令文本要精简得多,节省磁盘空间。
总结:工业界的最佳实践
目前绝大多数互联网公司的生产环境配置建议是:
- 开启混合持久化。
- AOF 设置为
everysec(每秒异步刷盘),兼顾性能与安全。 - 主节点 (Master) 负责响应请求,从节点 (Slave) 负责开启备份任务,防止
fork时的性能抖动影响业务。
Redis主从
主从Redis配置
批量拷贝
echo /tmp/7001/ /tmp/7002/ /tmp/7003/ | xargs -n 1 cp /usr/local/src/redis-6.2.6/redis.conf
实例配置:修改端口和dir文件
sed -i -e 's/6379/7001/g' -e 's/dir .//dir /tmp/7001//g' 7001/redis.conf
sed -i -e 's/6379/7002/g' -e 's/dir .//dir /tmp/7002//g' 7002/redis.conf
sed -i -e 's/6379/7003/g' -e 's/dir .//dir /tmp/7003//g' 7003/redis.conf
批量为每个redis绑定ip
逐一执行
进入你存放 7001-7003 的目录下执行
sed -i 's/192.168.150.101/192.168.100.128/g' 7001/redis.conf
sed -i 's/192.168.150.101/192.168.100.128/g' 7002/redis.conf
sed -i 's/192.168.150.101/192.168.100.128/g' 7003/redis.conf
或者一键修改
printf '%s\n' 7001 7002 7003 | xargs -I{} -t sed -i '1a replica-announce-ip 192.168.150.101' {}/redis.conf
分别启动
[root@bogon ~]# cd /tmp/
[root@bogon tmp]# redis-server 7001/redis.conf
[root@bogon ~]# cd /tmp/
[root@bogon tmp]# redis-server 7002/redis.conf
[root@bogon ~]# cd /tmp/
[root@bogon tmp]# redis-server 7003/redis.conf
开启主从
[root@bogon src]# redis-cli -p 7002
127.0.0.1:7002> ping
(error) NOAUTH Authentication required.
127.0.0.1:7002> auth 123321
OK
127.0.0.1:7002> BGREWRITEAOF
Background append only file rewriting started
127.0.0.1:7002> ping
PONG
127.0.0.1:7002> SLAVEOF 192.168.150.101 7001
或者
[root@bogon src]# redis-cli -p 7003 -a 123321
Warning: Using a password with '-a' or '-u' option on the command line interface may not be safe.
127.0.0.1:7003> REPLICAOF 192.168.150.101 7001
OK
127.0.0.1:7003>
但是如果主节点有密码,需要先在redis-cli -p 7002之后先确认主节点密码
[root@bogon src]# redis-cli -p 7002 -a 123321
Warning: Using a password with '-a' or '-u' option on the command line interface may not be safe.
127.0.0.1:7002> config set masterauth 123321
OK
127.0.0.1:7002> SLAVEOF 192.168.150.101 7001
OK Already connected to specified master
总结
[root@bogon src]# redis-cli -p 7003 -a 123321
Warning: Using a password with '-a' or '-u' option on the command line interface may not be safe.
127.0.0.1:7003> config set masterauth 123321
OK
127.0.0.1:7003> SLAVEOF 192.168.100.128 7001
OK
127.0.0.1:7003>
[root@bogon src]# redis-cli -p 7002 -a 123321
Warning: Using a password with '-a' or '-u' option on the command line interface may not be safe.
127.0.0.1:7002> config set masterauth 123321
OK
127.0.0.1:7002> SLAVEOF 192.168.100.128 7001
OK
127.0.0.1:7002>
[root@bogon src]# redis-cli -p 7001 -a 123321
Warning: Using a password with '-a' or '-u' option on the command line interface may not be safe.
127.0.0.1:7001> info replication
Replication
role:master
connected_slaves:2
slave0:ip=192.168.150.101,port=7003,state=online,offset=28,lag=1
slave1:ip=192.168.150.101,port=7002,state=online,offset=28,lag=0
master_failover_state:no-failover
master_replid:85dcb5138efcd395d3e10c0cea559641aff72efe
master_replid2:0000000000000000000000000000000000000000
master_repl_offset:28
second_repl_offset:-1
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:1
repl_backlog_histlen:28
主节点可读可写,从节点只能读
主从Redis原理(全量同步和增量同步)
如果id一致,第一次来就全量同步;如果id不一致,不是第一次来就增量同步
repl_baklog本质是环形数组(会覆盖原来,所以超过上限可能丢失数据;所以超过之后执行全量同步)
主从同步优化
优化全量同步性能
无盘模式
正常全量同步是在磁盘(不是内存)中生成一个rdb文件,然后通过网络把磁盘里面这个rdb文件发送
repl-diskless-sync
无盘模式(repl-diskless-sync yes):
- 主节点直接 Fork 一个子进程。
- 子进程直接扫描内存。
- 子进程一边扫描,一边把数据按照 RDB 的格式转换成二进制流。
- 这个流直接通过网络 Socket 发送给从节点,中间不落盘,也不在主节点内存里缓存整个文件。
这种模式主要针对以下特殊场景:
- 磁盘很慢但网络很快:比如你的磁盘是老旧的 HDD,或者是云服务器上性能较差的云盘,但你拥有万兆(10Gbps)带宽。
- 减少损耗:频繁的磁盘写入(特别是 SSD)会减少硬件寿命。
这里有一个细节:主节点虽然“无盘”了,但从节点默认还是“有盘”的。
- 主节点:不写磁盘,直接发流。
- 从节点:接收到这个流后,通常会先把它存到自己本地的磁盘上(生成
dump.rdb),然后再加载到内存里。
(注:从节点也可以通过设置 repl-diskless-load 来实现完全不写磁盘,直接在内存中解析,但这会有一定的风险,因为一旦解析失败,从节点原来的旧数据已经删了,新数据又没载入成功。)
控制redis单节点内存上限,少一点内存,减少rdb
提高repl_baklog大小
发现slave宕机就尽快鼓掌恢复,尽可能避免全量同步
主-从-从链式结构,限制一个主节点上的从节点的个数
1. 正常状态下:命令传播 (Command Propagation)
当你看到数据“一改就立刻更新”时,它在 Redis 术语里叫 “命令传播”。
- 本质:它是基于长连接的 实时流式同步。
- 过程:主节点每执行一条写命令(如
SET、DEL),就会实时把这条命令打包发给 Slave。 - 特点:Slave 像“复读机”一样,主节点做什么,它就跟着做一遍。这种方式效率极高,因为它不需要重新生成 RDB,也不涉及复杂的对比。
2. 异常状态下:真正的“增量同步” (PSYNC)
你提到的“本质也是增量同步”在 连接闪断 这种场景下体现得最明显。 假设你的主从连接因为网络抖动断开了 10 秒,这 10 秒内主节点又写了 100 条数据。
以前的版本(Redis 2.8 之前): Slave 一重连,主节点就必须执行 BGSAVE 发送完整的 RDB 过来。即便只差一条数据,也要全量同步,非常低效。
现在的版本(Redis 2.8+): 主从利用 PSYNC 命令 实现真正的增量同步。它依赖三个核心要素:
- 复制偏移量 (Offset):主节点和从节点各自记录一个数字,代表同步到了第几个字节。
- 主节点运行 ID (RunID):Slave 记得主节点的身份,防止重连后认错人。
- 复制积压缓冲区 (Replication Backlog Buffer):
- 主节点内存中有一个环形缓冲区(默认 1MB)。
- 主节点在发送命令给 Slave 的同时,也会把命令存进这个缓冲区。
- Slave 重连后:它会对主节点说:“我的 Offset 是 1000,你那现在是多少?”
- 主节点判断:如果主节点的 Offset 是 1100,且多出来的这 100 字节还在缓冲区里,主节点就只发这 100 字节给 Slave。
这就是你理解的本质上的增量同步(部分重同步)。
3.总结一下
• 平时连接好时:是“命令传播”(像直播,实时发数据)。
• 断开重连且数据量不大时:是“增量同步”(补发缓冲区里漏掉的那段)。
• 断开太久,缓冲区被覆盖了:只能降级为“全量同步”(重新发 RDB)。
💡 一个有趣的细节
你可以通过命令观察这个“缓冲区”: 在主节点输入 INFO Replication,你会看到:
• repl_backlog_size: 缓冲区总大小。
• repl_backlog_histlen: 当前缓冲区里存了多少数据。
如果你的主从连接经常断开并导致全量同步(你可以看日志里有没有写 RDB),那就说明你的 repl_backlog_size 设小了,需要调大。
哨兵机制
哨兵机制搭建
-
在/tmp/下 mkdir s1 s2 s3
-
在s1目录下创建一个sentinel.conf文件 vi s1/sentinel.conf,添加下面的内容:
rt 27001 sentinel announce-ip 192.168.100.128 sentinel monitor mymaster 192.168.100.128 7001 2 sentinel down-after-milliseconds mymaster 5000 sentinel failover-timeout mymaster 60000 dir "/tmp/s1"解读:
port 27001:是当前sentinel实例的端口sentinel monitor mymaster 192.168.150.101 7001 2:指定主节点信息mymaster:主节点名称,自定义,任意写192.168.150.101 7001:主节点的ip和端口2:选举master时的quorum值
-
拷贝配置文件
echo s2 s3 | xargs -t -n 1 cp s1/sentinel.conf
-
修改s2和s3中配置文件的端口
sed -i -e 's/27001/27002/g' -e 's/s1/s2/g' s2/sentinel.conf
sed -i -e 's/27001/27003/g' -e 's/s1/s3/g' s3/sentinel.conf -
启动
第1个
redis-sentinel s1/sentinel.conf
第2个
redis-sentinel s2/sentinel.conf
第3个
redis-sentinel s3/sentinel.conf
-
停机测试,关停7001
redis-cli -p 7001 -a 123321 shutdown
注意
- slave7002和7003要配置
replicaof 192.168.100.128 7001
masterauth 123321
replica-announce-ip 192.168.100.128
- 如果哨兵s2和s3的配置文件是复制的,且复制之前s1已经运行过,id会重复,需要全都删除,再重新启动就会自动生成三个不同的id
- 所以即使一开始是主节点也要配置
[root@bogon tmp]# redis-cli -p 7001 -a 123321 config set masterauth 123321因为有可能待会就宕机变成从节点
RedisTemplate的哨兵模式
打开文件
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
spring:redis:sentinel:master: mymasternodes:- 192.168.100.128:27001- 192.168.100.128:27002- 192.168.100.128:27003password: 123321
简化为
技术栈:
-
return一个抽象接口时需要用匿名内部类来重写其中方法;建议用lamda表达式优化。
-
customize 是 Spring Boot 提供的一个“插队”方法(或者叫钩子函数)。
在 Spring Boot 框架里,它的名字已经说明了一切:Customize = “按需定制”。
1.return一个抽象接口时需要用匿名内部类来重写其中方法;建议用lamda表达式优化。
@Bean
public LettuceClientConfigurationBuilderCustomizer clientConfigurationBuilderCustomizer(){return clientConfigurationBuilder -> {//优先从从节点读clientConfigurationBuilder.readFrom(ReadFrom.REPLICA_PREFERRED);};
}
2.customize 是 Spring Boot 提供的一个“插队”方法(或者叫钩子函数)。
在 Spring Boot 框架里,它的名字已经说明了一切:Customize = “按需定制”。
package cn.itcast.redisdemo;import io.lettuce.core.ReadFrom;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.data.redis.LettuceClientConfigurationBuilderCustomizer;
import org.springframework.context.annotation.Bean;
import org.springframework.data.redis.connection.lettuce.LettuceClientConfiguration;@SpringBootApplication
public class RedisDemoApplication {public static void main(String[] args) {SpringApplication.run(RedisDemoApplication.class, args);}@Beanpublic LettuceClientConfigurationBuilderCustomizer clientConfigurationBuilderCustomizer(){return clientConfigurationBuilder -> {//优先从从节点读clientConfigurationBuilder.readFrom(ReadFrom.REPLICA_PREFERRED);};}
}
总结
先尝试连接哨兵,发现多个哨兵,选一个哨兵建议连接;
订阅dispatching command SubscriptionCommand,获取redis集群的真实地址;
订阅之后哨兵就会把这个精确信息发给客户端,先得到master的redis的信息,再得到一个数组,封装了从节点salve的信息
同时连接主从节点
查询:连接从节点
如果:master宕机,切换;java客户端会再次尝试连接哨兵集群,通过哨兵重新连接redis集群
增删改:交给主节点
Redis分片集群
步骤
-
进入/tmp目录
cd /tmp
删除旧的,避免配置干扰
rm -rf 7001 7002 7003
创建目录
mkdir 7001 7002 7003 8001 8002 8003
-
在/tmp下准备一个新的redis.conf文件,内容如下:
port 6379
开启集群功能
cluster-enabled yes
集群的配置文件名称,不需要我们创建,由redis自己维护
cluster-config-file /tmp/6379/nodes.conf
节点心跳失败的超时时间
cluster-node-timeout 5000
持久化文件存放目录
dir /tmp/6379
绑定地址
bind 0.0.0.0
让redis后台运行
daemonize yes
注册的实例ip
replica-announce-ip 192.168.150.101
保护模式
protected-mode no
数据库数量
databases 1
日志
logfile /tmp/6379/run.log
-
将这个文件拷贝到每个目录下:
echo 7001 7002 7003 8001 8002 8003 | xargs -t -n 1 cp redis.conf
-
修改每个目录下的redis.conf,将其中的6379修改为与所在目录一致:
printf '%s\n' 7001 7002 7003 8001 8002 8003 | xargs -I{} -t sed -i 's/6379/{}/g' {}/redis.conf
-
一键启动
printf '%s\n' 7001 7002 7003 8001 8002 8003 | xargs -I{} -t redis-server {}/redis.conf
-
查看状态
ps -ef | grep redis
-
关闭所有进程
printf '%s\n' 7001 7002 7003 8001 8002 8003 | xargs -I{} -t redis-cli -p {} shutdown
-
进入redis的src目录
创建集群
redis-cli --cluster create --cluster-replicas 1 192.168.100.128:7001 192.168.100.128:7002 192.168.100.128:7003 192.168.100.128:8001 192.168.100.128:8002 192.168.100.128:8003
命令说明:
redis-cli --cluster或者./redis-trib.rb:代表集群操作命令create:代表是创建集群--replicas 1或者--cluster-replicas 1:指定集群中每个master的副本个数为1,此时节点总数 ÷ (replicas + 1)得到的就是master的数量。因此节点列表中的前n个就是master,其它节点都是slave节点,随机分配到不同master
-
查看集群状态
redis-cli -p 7001 cluster nodes
Redis 节点的端口分配有一个固定的规则:总线端口 = 数据端口 + 10000。
• 数据端口 (7002):这是我们平时使用的端口,用来读写数据、处理 redis-cli 的请求。
• 总线端口 (17002):这是 Redis 节点之间“私聊”用的端口。
散列插槽
集群模式下启动
redis-cli -c -p 7001要加上-c进入控制台,-c表示可以重定向
集群伸缩
添加节点到集群
redis-cli --cluster add-node 192.168.100.128:7004 192.168.100.128:7001
转移插槽分配
redis-cli --cluster reshard 192.168.100.128:7001
[root@bogon tmp]# redis-cli -p 7004 cluster nodes
e8b585d5e95575db3e1bc4bc825f796f9b0827dd 192.168.100.128:7004@17004 myself,master - 0 1768564434000 7 connected 0-2999
a9566a4d801ca50585f62f606729da8c3b415a18 192.168.100.128:7002@17002 master - 0 1768564436000 2 connected 5461-10922
1bde8c3b9570df77f4f3ed1b9458796c5a9ae77d 192.168.100.128:8001@18001 slave e4d7b85516173e6e6604eee9fbf0aeec0f990ebd 0 1768564434644 1 connected
e4d7b85516173e6e6604eee9fbf0aeec0f990ebd 192.168.100.128:7001@17001 master - 0 1768564435000 1 connected 3000-5460
449e7b0b76e71149611d7c36d25fffbb291d6758 192.168.100.128:8003@18003 slave 0c5b13b6228708c5c8b7bfb9c2cfd01734f2bd93 0 1768564436559 3 connected
02104f52a720282377b68352e0b088ecc330b1e6 192.168.100.128:8002@18002 slave a9566a4d801ca50585f62f606729da8c3b415a18 0 1768564435955 2 connected
0c5b13b6228708c5c8b7bfb9c2cfd01734f2bd93 192.168.100.128:7003@17003 master - 0 1768564436000 3 connected 10923-16383
[root@bogon tmp]# redis-cli -c -p 7001
127.0.0.1:7001> get num
-> Redirected to slot [2765] located at 192.168.100.128:7004
"123"
192.168.100.128:7004> set num 10
OK
删除集群中的节点
删除一个带槽节点的标准流程分为两步:迁移槽位(Reshard) 和 正式删除(Del-node)。

第一步:迁移槽位 (Resharding)
你需要把 7004 拥有的 0-2999 槽位平摊给其他 Master 节点(比如 7001, 7002, 7003)。
- 执行迁移命令:
Bash
Copy code
redis-cli --cluster reshard 192.168.100.128:7001
注意:IP 端口可以是集群中任何一个存活的节点。 - 按照提示输入参数:
• How many slots do you want to move? 输入 3000 (你要迁出的槽位总数)。
• What is the receiving node ID? 输入目标节点的 ID(例如 7001 的 ID)。你想把这 3000 个槽给谁,就填谁的 ID。
• Source node IDs? 输入 7004 的节点 ID。
• Done? 输入 done。 - 确认迁移: 输入 yes。Redis 会开始把数据从 7004 搬运到目标节点。

第二步:删除节点 (Del-node)
当 7004 变成一个“空节点”(不再持有任何槽位)后,你就可以安全地把它踢出集群了。 - 执行删除命令:
Bash
Copy code
格式:redis-cli --cluster del-node <集群中任意IP:PORT> <要删除的节点ID>
redis-cli --cluster del-node 192.168.100.128:7001 <7004的Node_ID>
- 验证结果:
Bash
Copy code
redis-cli -c -p 7001 cluster nodes
此时你应该看不到 7004 节点了,且原有的 0-2999 槽位已经转移到了你指定的接收节点上。
故障转移
一般就用默认这个
主变从,从变主
[root@bogon tmp]# redis-cli -c -p 7002
127.0.0.1:7002> cluster failover