一、下载镜像
采用的是7.4.7的redis镜像
docker pull redis:7.4.7
二、概念与架构图
以下是哨兵模式的架构图
Sentinel1 Sentinel2 Sentinel3
| | |
+— Monitor + Vote —+
|
+—————–+
| Master |
| 6379 |
+—————–+
/ \\
/Replicate \\
v v
+———-+ +———-+
| Slave1 | | Slave2 |
| 6380 | | 6381 |
+———-+ +———-+
节点解释
| master主节点 | 1(可能 failover 后变化) | 提供写服务,数据来源中心 | 原始数据持久化 |
| Slave 从节点 | 2(或者更多) | 数据副本,提供读服务,故障接管备用 | 拷贝主节点数据 |
| Sentinel 哨兵节点 | 3 个或更多 | 健康监控、主从切换、服务发现,不存业务数据 | 只保存状态信息 |
1、Master(主节点)
职责:
- 所有写入操作 只会 写在主节点
- 更新数据会异步同步到所有从节点
- 当主节点宕机时,它是 failover 的核心对象
特点:
- 单节点写入性能高
- 存储的数据是最终来源
2、 Slave(从节点)
职责:
- 实时复制主节点的数据
- 支持读操作(读写分离)
- 主节点宕机时由哨兵提升某个从节点为新主节点(Failover)
特点:
- 提升读吞吐
- 数据备份冗余
- 不参与选主逻辑(由哨兵负责)
主节点恢复后会自动变为新的 Slave —— 哨兵强制一致性规则
3、Sentinel(哨兵节点)
职责:
- 监控(Monitoring):自动监控主从的状态存活
- 通知(Notification):当节点异常会通知管理员或客户端
- 故障迁移(Failover):决定并执行主从切换
- 服务发现(Service Discovery):向客户端提供新主节点地址
特点:
- 无数据存储
- 需要数量至少 3 个(保证多节点投票)
- 通过多数票判断主节点是否下线(避免误判)
4、Failover(故障转移)机制
主节点宕机后:
1、哨兵投票达成一致:主节点真的挂了(quorum 达成)
2、选出最佳 Slave 提升为 Master
3、通知其他 Slave 复制新的 Master
4、客户端无需修改:通过 Sentinel 获取新主节点地址
三、部署
两台服务器的部署方案
192.168.244.130
192.168.244.131
创建自定义网络
docker network create redis-net
部署的文件目录如下,因为哨兵节点需要将临时文件写入配置文件的所在目录,所以单独存放哨兵节点的配置文件
A服务器的文件
redis–sentinel/
├─ docker–compose.yml
├─ config
│ ├─ master
│ ├─ redis.conf ← 主节点
│ ├─ server1
│ ├─ redis.conf ← 从节点
│ ├─ sentinel1
│ ├─ sentinel.conf ← 哨兵配置文件
│ ├─ sentinel2
│ ├─ sentinel.conf ← 哨兵配置文件
└─ data
├─ master
├─ slave2
├─ slave3
B服务器的文件目录
redis–sentinel/
├─ docker–compose.yml
├─ config
│ ├─ server2
│ ├─ redis.conf ← 从节点
│ ├─ sentinel3
│ ├─ sentinel.conf ← 哨兵配置文件
└─ data
├─ slave2
1、A服务器的部署文件
(1)主节点配置文件
主节点配置文件redis.conf
# 绑定到所有网卡,使容器内其他服务可访问
bind 0.0.0.0
# Redis 服务端口(由 docker-compose 覆盖)
port 6379
# 保护模式关闭(允许远程访问,生产环境建议开启密码)
protected-mode no
# Redis 是否后台运行
daemonize no
# 持久化:开启 AOF(实时写磁盘,数据安全性更高)
appendonly yes
# AOF 文件路径(默认存储到挂载的 /data 目录)
appendfilename "appendonly.aof"
# 对外IP必须是宿主机IP地址,否则哨兵将无法获取主节点,端口也是如此(重要)
replica-announce-ip 192.168.244.130
replica-announce-port 6379
# 主从复制时使用 docker-compose 启动参数 –replicaof 自动配置
# replicaof <master-ip> <master-port>
# 在 compose 中已配置,不需在此填写
(2)从节点配置文件
从节点(server1)的配置文件redis.conf
# 绑定到所有网卡,使容器内其他服务可访问
bind 0.0.0.0
# Redis 服务端口(由 docker-compose 覆盖)
port 6380
# 保护模式关闭(允许远程访问,生产环境建议开启密码)
protected-mode no
# Redis 是否后台运行
daemonize no
# 持久化:开启 AOF(实时写磁盘,数据安全性更高)
appendonly yes
# AOF 文件路径(默认存储到挂载的 /data 目录)
appendfilename "appendonly.aof"
# 对外IP必须是宿主机IP地址,否则哨兵将无法获取主节点,端口也是如此(重要)
replica-announce-ip 192.168.244.130
replica-announce-port 6380
# 主从复制时使用 docker-compose 启动参数 –replicaof 自动配置
# replicaof <master-ip> <master-port>
# 在 compose 中已配置,不需在此填写
(3)哨兵节点配置文件
1号哨兵节点(sentinel1)配置sentinel.conf
port 26379
daemonize no
protected-mode no
# 监控主节点,IP地址为主节点对应的宿主机IP地址(重要)
sentinel monitor mymaster 192.168.244.130 6379 2
# 主节点超时时间
sentinel down-after-milliseconds mymaster 5000
# 故障转移最大允许时间
sentinel failover-timeout mymaster 10000
# 绑定到所有网卡
bind 0.0.0.0
# (如有密码)
# sentinel auth-pass mymaster yourpassword
# 指定哨兵对外的IP地址,通过这IP地址让其他服务找到该哨兵节点(重要)
sentinel announce-ip "192.168.244.130"
sentinel announce-port 26379
2号哨兵节点(sentinel2)配置sentinel.conf
port 26380
daemonize no
protected-mode no
# 监控主节点,IP地址为主节点对应的宿主机IP地址(重要)
sentinel monitor mymaster 192.168.244.130 6379 2
# 主节点超时时间
sentinel down-after-milliseconds mymaster 5000
# 故障转移最大允许时间
sentinel failover-timeout mymaster 10000
# 绑定到所有网卡
bind 0.0.0.0
# (如有密码)
# sentinel auth-pass mymaster yourpassword
# 指定哨兵对外的IP地址,通过这IP地址让其他服务找到该哨兵节点(重要)
sentinel announce-ip "192.168.244.130"
sentinel announce-port 26380
(4)docker-compose文件
services:
redis-master:
image: redis:7.4.7
container_name: redis–master
restart: always
ports:
– "6379:6379"
volumes:
– ./config/master/redis.conf:/usr/local/etc/redis/redis.conf
– ./data/master:/data
command: ["redis-server", "/usr/local/etc/redis/redis.conf"]
networks:
– redis–net
healthcheck:
test: ["CMD", "redis-cli", "ping"]
interval: 3s
timeout: 5s
retries: 5
start_period: 3s
redis-slave1:
image: redis:7.4.7
container_name: redis–slave1
restart: always
ports:
– "6380:6380"
depends_on:
redis-master:
condition: service_healthy
volumes:
– ./config/slave1/redis.conf:/usr/local/etc/redis/redis.conf
– ./data/slave1:/data
command: ["redis-server", "/usr/local/etc/redis/redis.conf", "–replicaof", "192.168.244.130", "6379"]
networks:
– redis–net
redis-sentinel1:
image: redis:7.4.7
container_name: redis–sentinel1
depends_on:
redis-master:
condition: service_healthy
ports:
– "26379:26379"
volumes:
– ./config/sentinel1:/usr/local/etc/redis
– ./data/sentinel1:/data
command: ["redis-sentinel", "/usr/local/etc/redis/sentinel.conf"]
networks:
– redis–net
redis-sentinel2:
image: redis:7.4.7
container_name: redis–sentinel2
depends_on:
redis-master:
condition: service_healthy
ports:
– "26380:26380"
volumes:
– ./config/sentinel2:/usr/local/etc/redis
– ./data/sentinel2:/data
command: ["redis-sentinel", "/usr/local/etc/redis/sentinel.conf"]
networks:
– redis–net
networks:
redis-net:
external: true
name: redis–net
docker-compose命令(新版本的docker-compose)
# 启动
docker compose up -d
# 删除
docker compose down -v # 取消挂载
2、B服务器的部署文件
(1)从节点配置文件
从节点(server2)的配置文件redis.conf
# 绑定到所有网卡,使容器内其他服务可访问
bind 0.0.0.0
# Redis 服务端口(由 docker-compose 覆盖)
port 6379
# 保护模式关闭(允许远程访问,生产环境建议开启密码)
protected-mode no
# Redis 是否后台运行
daemonize no
# 持久化:开启 AOF(实时写磁盘,数据安全性更高)
appendonly yes
# AOF 文件路径(默认存储到挂载的 /data 目录)
appendfilename "appendonly.aof"
# 对外IP必须是宿主机IP地址,否则哨兵将无法获取主节点,端口也是如此(重要)
replica-announce-ip 192.168.244.131
replica-announce-port 6379
# 主从复制时使用 docker-compose 启动参数 –replicaof 自动配置
# replicaof <master-ip> <master-port>
# 在 compose 中已配置,不需在此填写
(2)哨兵节点配置文件
3号哨兵节点(sentinel3)配置sentinel.conf
port 26379
daemonize no
# 启用主机名解析
# sentinel resolve-hostnames yes
protected-mode no
# dir "/tmp"
# 监控主节点(使用服务名,不要硬编码 IP)
sentinel monitor mymaster 192.168.244.130 6379 2
# 主节点超时时间
sentinel down-after-milliseconds mymaster 5000
# 故障转移最大允许时间
sentinel failover-timeout mymaster 10000
# 绑定到所有网卡
bind 0.0.0.0
# (如有密码)
# sentinel auth-pass mymaster yourpassword
# 指定哨兵对外的IP地址,通过这IP地址让其他服务找到该哨兵节点(重要)
sentinel announce-ip "192.168.244.131"
sentinel announce-port 26379
(3)docker-compose文件
services:
redis-slave2:
image: redis:7.4.7
container_name: redis–slave2
restart: always
ports:
– "6379:6379"
volumes:
– ./config/slave2/redis.conf:/usr/local/etc/redis/redis.conf
– ./data/slave2:/data
command: ["redis-server", "/usr/local/etc/redis/redis.conf", "–replicaof", "192.168.244.130", "6379"]
networks:
– redis–net
redis-sentinel3:
image: redis:7.4.7
container_name: redis–sentinel3
ports:
– "26381:26381"
volumes:
– ./config/sentinel3:/usr/local/etc/redis
– ./data/sentinel3:/data
command: ["redis-sentinel", "/usr/local/etc/redis/sentinel.conf"]
networks:
– redis–net
networks:
redis-net:
external: true
name: redis–net
docker compose命令(新版本的docker-compose)
# 启动
docker compose up -d
# 删除
docker compose down -v # 取消挂载
3、注意点
(1)容器启动时会出现容器因为权限问题无法读取配置文件的情况,需要给配置文件赋予权限
# 赋予目录权限
chmod 777 sentinel
# 赋予文件权限
chmod 666 sentinel.conf
(2)在主节点未启动完成之前其他节点暂时不要启动
四、验证
使用命令查看哨兵节点的日志(以redis-sentinel1为例)
docker logs -f redis-sentinel1
日志如下则成功启动,并且已经连接到了主节点
1:X 09 Dec 2025 03:51:45.194 # WARNING Memory overcommit must be enabled! Without it, a background save or replication may fail under low memory condition. Being disabled, it can also cause failures without low memory condition, see https://github.com/jemalloc/jemalloc/issues/1328. To fix this issue add 'vm.overcommit_memory = 1' to /etc/sysctl.conf and then reboot or run the command 'sysctl vm.overcommit_memory=1' for this to take effect.
1:X 09 Dec 2025 03:51:45.197 * oO0OoO0OoO0Oo Redis is starting oO0OoO0OoO0Oo
1:X 09 Dec 2025 03:51:45.197 * Redis version=7.4.7, bits=64, commit=00000000, modified=0, pid=1, just started
1:X 09 Dec 2025 03:51:45.197 * Configuration loaded
1:X 09 Dec 2025 03:51:45.197 * monotonic clock: POSIX clock_gettime
1:X 09 Dec 2025 03:51:45.198 * Running mode=sentinel, port=26379.
1:X 09 Dec 2025 03:51:45.199 * Sentinel ID is 8bb480d5b1d1fff329534943c19571eb7aad38c7
1:X 09 Dec 2025 03:51:45.199 # +monitor master mymaster 192.168.244.130 6379 quorum 2
1:X 09 Dec 2025 03:51:55.216 * +convert-to-slave slave 172.21.0.1:6379 172.21.0.1 6379 @ mymaster 192.168.244.130 6379
1:X 09 Dec 2025 03:52:01.507 * +fix-slave-config slave 172.21.0.6:6379 172.21.0.6 6379 @ mymaster 192.168.244.130 6379
1:X 09 Dec 2025 03:52:01.507 * +fix-slave-config slave 172.21.0.7:6379 172.21.0.7 6379 @ mymaster 192.168.244.130 6379
通过 redis-cli 客户端检查各个节点的情况
1、基本命令
(1)查看哨兵监控的主节点信息
docker exec redis-sentinel1 redis-cli -p 26379 SENTINEL masters
(2)查看指定主节点下的从节点列表
docker exec redis-sentinel1 redis-cli -p 26379 SENTINEL slaves <master-name>
(3)查看当前监控该主节点的 Sentinel 节点列表(同一个主节点可能被多个 Sentinel 监控)
docker exec redis-sentinel1 redis-cli -p 26379 SENTINEL sentinels <master-name>
(4)查看哨兵提供的主节点对外的IP地址和端口
docker exec <sentinel-name> redis-cli -p 26379 sentinel get-master-addr-by-name mymaster
docker exec redis-sentinel1 redis-cli -p 26379 sentinel get-master-addr-by-name mymaster
返回的是主节点的对外IP和端口,例如下面这样才是正确的,这样才能让其他服务找到主节点
192.168.244.130
6379
返回的错误示例如下,IP是docker网络的IP,则将会导致其他服务器上的服务找不到主节点,无法解析这个IP
172.20.0.3
6379
注意点:这里返回的IP必须是宿主机的IP地址,这样开发环境才能正确的获取到主节点,否则开发环境将会无法连接到主节点上,其他服务器也是同理
(6)查询节点的信息
# 通过redis-cli客户端进行查询
redis-cli -h 192.168.244.130 -p 6379 info replication
# 主节点节点信息
role:master # 节点角色(主节点)
connected_slaves:2 # 从节点数量
slave0:ip=172.20.0.3,port=6379,state=online,offset=25203,lag=0
slave1:ip=172.20.0.4,port=6379,state=online,offset=24933,lag=0
master_failover_state:no-failover
master_replid:e0164bb431139668db4329b89535050599d46ae4
master_replid2:0000000000000000000000000000000000000000
master_repl_offset:25338
second_repl_offset:-1
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:1
repl_backlog_histlen:25338
# 从节点信息
role:slave
master_host:redis-master
master_port:6379
master_link_status:up
master_last_io_seconds_ago:1
master_sync_in_progress:0
slave_read_repl_offset:41650
slave_repl_offset:41650
slave_priority:100
slave_read_only:1
replica_announced:1
connected_slaves:0
master_failover_state:no-failover
master_replid:e0164bb431139668db4329b89535050599d46ae4
master_replid2:0000000000000000000000000000000000000000
master_repl_offset:41650
second_repl_offset:-1
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:713
repl_backlog_histlen:40938
2、测试
测试哨兵模式是否生效,关闭现在的主节点,通过命令查看主节点是否有被切换,如果被切换说明没有问题
docker stop redis-master
3、踩坑
(1)不在同一个docker网络下的服务访问不到哨兵节点
检查哨兵节点的配置文件sentinel.conf,其中的IP设置是否正确
(2)本地开发环境无法访问到哨兵节点返回的主节节点,例如如下报错
Unable to connect to 172.20.0.5/<unresolved>:6379
这种情况是因为 配置文件 redis.conf没有配置如下参数,或者参数配置错误
# 让哨兵通过这个IP和端口找到宿主机并且返回给其他需要redis的服务
replica-announce-ip 192.168.244.130
replica-announce-port 6379
(3)更新了配置文件却还是不生效,出现以上两个问题
这是由于sentinel.conf 有残留的缓存引发的,因为哨兵节点在运行时会更改这个配置文件,会有自动生成的配置,需要将这个配置文件恢复为最初的样子并保留自己的配置,并将 data 目录下产生的数据文件全部清理,再重新启动集群就可以解决
五、整合springboot
使用springboot的版本为3.5.7
1、pom.xml
pom文件依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
2、application.yml
在配置文件中添加如下配置,更多配置请自行查询
spring:
data:
redis:
sentinel:
# 主节点名称
master: mymaster
# 哨兵节点
nodes:
– 192.168.244.130:26379
– 192.168.244.130:26380
– 192.168.244.131:26379
3、反序列化配置
RedisConfig.java
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;
@Configuration
public class RedisConfig {
@Bean
public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) {
RedisTemplate<String, Object> template = new RedisTemplate<>();
template.setConnectionFactory(factory);
StringRedisSerializer stringSerializer = new StringRedisSerializer();
// 推荐写法:直接传入 ObjectMapper
ObjectMapper mapper = new ObjectMapper();
mapper.findAndRegisterModules();
mapper.activateDefaultTyping(
mapper.getPolymorphicTypeValidator(),
ObjectMapper.DefaultTyping.NON_FINAL
);
GenericJackson2JsonRedisSerializer jsonSerializer =
new GenericJackson2JsonRedisSerializer(mapper);
// 设置 key & value 序列化方式
template.setKeySerializer(stringSerializer);
template.setHashKeySerializer(stringSerializer);
template.setValueSerializer(jsonSerializer);
template.setHashValueSerializer(jsonSerializer);
template.afterPropertiesSet();
return template;
}
}
4、测试
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.data.redis.core.RedisTemplate;
@SpringBootTest
class RedisBootDemoApplicationTests {
@Autowired
private RedisTemplate<String, Object> redisTemplate;
@Test
void contextLoads() {
}
@Test
void setValue() {
redisTemplate.opsForValue().set("test01", "hello redis-sentinel");
}
@Test
void getValue() {
String test01 = (String) redisTemplate.opsForValue().get("test01");
System.out.println("获取值: " + test01);
}
}
网硕互联帮助中心





评论前必须登录!
注册