🎯 引言:Redis的高可用性为啥这么重要?
在现代高可用系统中,Redis 是一款不可或缺的分布式缓存与数据库系统。无论是提升访问速度,还是实现数据的高效持久化,Redis 都能轻松搞定。可是,当你把 Redis 用于关键业务时,它的高可用性就显得尤为重要。为了避免系统出现单点故障,保障业务连续性,我们需要有一套高可用架构来确保 Redis 的稳定运行。
今天,我们就一起来探讨一下 Redis 高可用架构 的几种实现方式,看看如何设计一套健壮的 Redis 高可用方案,保证我们的 Redis 缓存一直在线,稳定运行。
🛠️ 1. Redis 主从复制:简单的高可用方案
1.1 主从复制工作原理
Redis 主从复制(Master-Slave Replication)是 Redis 实现高可用性最基础的方案之一。在这种架构中,你会有一个主节点(Master)和一个或多个从节点(Slave)。主节点负责处理写操作和数据更新,而从节点则负责从主节点同步数据并进行只读操作。
当主节点发生故障时,可以将某个从节点提升为主节点(手动或自动),确保系统的持续可用。
主从复制基本架构
- 主节点(Master) :处理所有的写操作(如
SET
、DEL
等命令)。 - 从节点(Slave) :只处理读操作(如
GET
命令),并定期从主节点同步数据。
主从复制工作流程
(1)初始同步
当从节点(Slave)首次启动并连接到主节点时,它需要将主节点的数据完全同步过来。这个过程称为“全量同步”。
- 从节点连接主节点:从节点向主节点发起连接请求。
- 主节点进行快照(RDB) :主节点执行
SAVE
或BGSAVE
命令,将当前的内存数据(RDB 快照)保存到磁盘。 - 从节点复制快照:主节点将保存的数据发送给从节点,从节点将数据写入本地数据库。
- 建立同步通道:从节点在完成全量同步后,开始监听主节点的变更。
(2)增量同步
从节点已经有了主节点的数据快照后,接下来的数据同步就是增量同步,只同步主节点上的变更数据。
- 主节点记录变更操作:主节点每处理一个写操作(如
SET
、DEL
),会将这个操作写入复制日志(replication log
)中。 - 主节点发送变更命令给从节点:主节点将增量变更命令发送到所有的从节点。
- 从节点执行命令:从节点收到变更命令后,会在本地执行这些命令,保证数据的一致性。
(3)断线恢复
如果从节点与主节点的连接丢失,Redis 会进行断线恢复:
- 当从节点重新连接到主节点时,Redis 会判断是进行全量同步还是增量同步。如果从节点没有数据快照,它会进行全量同步;如果有数据快照,则会通过增量同步方式进行恢复。
主从复制的特点
- 只读模式:从节点默认只能进行读取操作(
GET
),不接受写操作。写操作必须通过主节点执行。 - 延迟问题:由于从节点是从主节点获取数据的,主节点的写操作会有一定的延迟才会在从节点上看到。
- 数据一致性:主从复制是最终一致性的,意味着从节点上的数据不会立即与主节点同步一致,在数据变更时可能会有短暂的不一致状态。
主从复制的优势
- 提高可用性和可靠性:从节点可以用来分担主节点的读取压力,还可以在主节点故障时迅速切换,保证服务的可用性。
- 数据备份:从节点可以作为主节点的备份,提供数据冗余,防止主节点数据丢失。
- 负载均衡:多个从节点可以分担读取请求,从而降低主节点的负载。
主从复制使用场景
- 高可用架构:当主节点出现故障时,可以通过从节点提升一个新的主节点来实现故障切换。
- 读写分离:主节点处理写操作,从节点处理读操作,优化性能。
- 数据冗余:从节点可以作为数据的备份,一旦主节点出现问题,可以快速恢复数据。
1.2 主从复制搭建步骤
1、Redis 环境准备
下载安装Redis
本次演示使用的版本是6.2.7版本(并且在本机环境模拟3个redis示例演示)
进行Redis目录下创建 redis-cluster (mkdir redis-cluster)
复制原有redis.conf新建3个配置文件 redis-6379.conf redis-6380.conf redis-6381.conf
将配置文件放置到redis-cluster目录下
编辑主节点(redis-6379.conf),主要修改以下配置项
port 6379 bind 127.0.0.1 pidfile ./redis-cluster/redis-6379.pid logfile ./redis-cluster/redis-6379.log dir ./redis-cluster/data/redis-6379
编辑从节点(redis-6380.conf)
port 6380 bind 127.0.0.1 pidfile ./redis-cluster/redis-6380.pid logfile ./redis-cluster/redis-6380.log dir ./redis-cluster/data/redis-6380 replicaof 127.0.0.1 6379
编辑从节点(redis-6381.conf)
port 6381 bind 127.0.0.1 pidfile ./redis-cluster/redis-6381.pid logfile ./redis-cluster/redis-6381.log dir ./redis-cluster/data/redis-6381 replicaof 127.0.0.1 6379
配置完成,主要配置项说明:
- port:每个 Redis 实例需要不同的端口。
- pidfile:每个实例需要独立的 PID 文件。
- logfile:每个实例需要不同的日志文件。
- replicaof:设置主从复制指定主节点。
- dir:设置数据文件的存储路径。确保每个 Redis 实例的
dir
路径存在(提前创建,用于存储数据文件)
每个实例都需要不同的配置(特别是端口、PID 文件、日志文件和数据目录),确保它们不会冲突。
2、启动 Redis 实例
打开 命令终端
进入 Redis 所在的目录
启动3个实例
./src/redis-server ./redis-cluster/redis-6379.conf ./src/redis-server ./redis-cluster/redis-6380.conf ./src/redis-server ./redis-cluster/redis-6381.conf
检查实例是否正常
你可以通过 redis cli 连接到每个实例,确认它们是否成功启动。检查主从复制是否正常或者连接到主节点新增键值看同步是否成功
./src/redis-cli -p 6379 info replication ./src/redis-cli -p 6380 info replication
主节点
从节点
说明主从复制成功。
⚙️ 2. Redis Sentinel:自动故障转移的英雄
2.1 Redis Sentinel的工作原理
Redis Sentinel 是 Redis 提供的自动故障转移系统。它监控 Redis 实例的健康状况,一旦主节点发生故障,Sentinel 会自动将一个从节点提升为主节点,并更新所有从节点的信息,保证系统的高可用性。Sentinel 的作用不止于监控故障,它还可以提供自动故障转移、配置发布以及客户端连接的重定向。
哨兵部署架构
1、监控:Sentinel 会不断检查 master 和 slave 是否按预期工作。
2、自动故障恢复:如果 master 故障,Sentinel 会将一个 slave 提升为 master,当故障实例恢复后也会安装新的 master 为主节点。
3、通知:Sentinel 充当 Redis 客户端的服务发现来源,当集群发生故障转移时,会将最新节点信息推送给 Redis 客户端。
哨兵集群监控原理
Sentinel 基于心跳机制监测服务状态,每隔1秒向集群的每个实例发送ping命令:
- 主观下线:如果某 Sentinel 节点发现某实例未在规定时间响应,则认为该实例主观下线。
- 客观下线:若超过指定数量(quorum)的 Sentinel 都认为该实例主观下线,则该实例客观下线。quorum 值最好超过 Sentinel 实例数量的一半。
集群故障恢复原理
一旦发现 master 故障,Sentinel 需要在 salve 中选择一个作为新的 master,选择依据是这样的:
- 首先会判断 slave 节点与 master 节点断开时间长短 ,如果超过指定值(downafter-milliseconds * 10)则会排除该 slave 节点
- 然后判断 slave 节点的 slave-priority 值,越小优先级越高,如果是0则永不参与选举
- 如果slave-priority一样,则判断 slave 节点的 offset 值 ,越大说明数据越新,优先级越高
- 最后是判断slave节点的 运行id 大小,越小优先级越高。
当选出一个新的 master 之后,该如何实现切换呢?
流程如下:
- Sentinel给备选的 slave1 节点发送 slaveof no one 命令,让该节点成为 master
- Sentinel给所有其它 slave 发送 slaveof 127.0.0.1 6380 命令,让这些 slave 成为新 master 的从节点,开始从新的 master 上同步数据。
- 最后,Sentinel 将故障节点标记为 slave,当故障节点恢复后会自动成为新的 master 的 slave 节点。
2.2 配置 Redis Sentinel(哨兵)
部署多个 Redis 实例后,配置 Redis Sentinel 以实现高可用性,可以通过以下步骤来完成。哨兵模式的主要功能是监控 Redis 主节点和从节点的状态,并在主节点宕机时自动切换到从节点。
- 你已经有了多个 Redis 实例(例如
6379
、6380
、6381
)。 - 你希望配置 Sentinel 来监控这些实例,并在主节点宕机时进行自动故障转移。
每个 Redis 哨兵都需要配置一个 sentinel.conf
文件来监控主节点。你可以创建多个 Sentinel 配置文件,并通过它们来监控 Redis 实例。
设置哨兵配置文件(sentinel.conf
) 内容如下:
port 26379
bind 127.0.0.1
pidfile "./redis-cluster/sentinel-26379/sentinel-26379.pid"
logfile "./redis-cluster/sentinel-26379/sentinel-26379.log"
dir "./redis-cluster/sentinel-26379"
sentinel monitor mymaster 127.0.0.1 6379 2
sentinel down-after-milliseconds mymaster 5000
sentinel failover-timeout mymaster 10000
sentinel parallel-syncs mymaster 1
复制文件进行 26380 和 26381 哨兵的配置。
port 26380
bind 127.0.0.1
pidfile "./redis-cluster/sentinel-26380/sentinel-26380.pid"
logfile "./redis-cluster/sentinel-26380/sentinel-26380.log"
dir "./redis-cluster/sentinel-26380"
sentinel monitor mymaster 127.0.0.1 6380 2
sentinel down-after-milliseconds mymaster 5000
sentinel failover-timeout mymaster 10000
sentinel parallel-syncs mymaster 1
- sentinel monitor mymaster:这是 Sentinel 监控的主节点名称,这里设置为
mymaster
,并且指定了主节点的 IP 和端口(127.0.0.1 6379
)。后面的数字2
表示至少需要 2 个哨兵节点确认主节点宕机后,才会进行故障转移。 - sentinel down-after-milliseconds mymaster 5000:当主节点 5 秒没有响应时,认为该节点宕机。
- sentinel failover-timeout mymaster 10000:故障转移的超时时间。
- sentinel parallel-syncs mymaster 1:指定故障转移时,最多有多少个从节点同步数据。
2.3 启动 Redis Sentinel(哨兵)
你可以启动多个 Sentinel 实例来提高容错性。
./src/redis-sentinel ./redis-cluster/sentinel-26379.conf ./src/redis-sentinel ./redis-cluster/sentinel-26380.conf ./src/redis-sentinel ./redis-cluster/sentinel-26381.conf
验证哨兵状态查看是否发现主节点
./src/redis-cli -p 26379 sentinel masters
查看从节点状态
./src/redis-cli -p 26379 sentinel slaves mymaster
5. 搭建完成,这说明 Sentinel 已经自动发现并管理了从节点 🎯
2.4 哨兵模式连接Redis
在Sentinel集群监管下的Redis主从集群,其节点会因为自动故障转移而发生变化,Redis的客户端必须感知这种变化,及时更新连接信息。
客户端应用程序应该使用RedisSentinel模式连接Redis,而不是直接固定的Redis的IP。我们可以直接通过哨兵获取到当前的主节点IP。可以使用命令:
./src/redis-cli -p 26379 SENTINEL get-master-addr-by-name mymaster
我们通过一个测试来实现Spring Boot实现集成哨兵机制。
Maven依赖
<dependency>
<groupId>org.redisson</groupId>
<artifactId>redisson-spring-boot-starter</artifactId>
<version>3.15.3</version>
</dependency>
配置文件添加配置项
spring:
redis:
sentinel:
master: mymaster
nodes: 127.0.0.1:26379,127.0.0.1:26380,127.0.0.1:26381
host: 127.0.0.1
port: 6379
database: 1 # 默认使用 0 库
password:
测试连接代码
@Configuration
@AutoConfigureBefore({RedisAutoConfiguration.class, RedissonAutoConfiguration.class})
public class RedissonConfig {
protected final Logger logger = LoggerFactory.getLogger(this.getClass());
@Value("${spring.redis.host}")
private String host;
@Value("${spring.redis.port}")
private String port;
@Value("${spring.redis.password}")
private String password;
@Value("${spring.redis.database}")
private int database;
@Value("${spring.redis.sentinel.master}")
private String sentinelMaster;
@Value("${spring.redis.sentinel.nodes}")
private String sentinelNodes;
@Value("${redisson.threads:4}")
private int threads;
@Value("${redisson.nettyThreads:5}")
private int nettyThreads;
@Value("${redisson.connectPoolSize:20}")
private int connectPoolSize;
@Value("${redisson.connectPoolIdleSize:5}")
private int connectPoolIdleSize;
@Value("${redisson.connectTimeout:10000}")
private int connectTimeout;
@Value("${redisson.retryAttempts:3}")
private int retryAttempts;
@Value("${redisson.retryInterval:1500}")
private int retryInterval;
@Value("${redisson.timeout:10000}")
private int timeout;
@Value("${redisson.pingConnectionInterval:30000}")
private int pingConnectionInterval;
@Bean(destroyMethod = "shutdown")
@ConditionalOnMissingBean(RedissonClient.class)
public RedissonClient redisson() throws IOException {
Config config = new Config();
int cpuCores = Runtime.getRuntime().availableProcessors();
threads = (threads == 0 ? cpuCores * 2 : threads);
nettyThreads = (nettyThreads == 0 ? cpuCores : nettyThreads);
config.setThreads(threads);
config.setNettyThreads(nettyThreads);
config.setTransportMode(TransportMode.NIO);
// 设置序列化(禁用class类型信息)
config.setCodec(new SimpleRedisJsonCodec());
if (StringUtils.isNotEmpty(sentinelMaster) && StringUtils.isNotEmpty(sentinelNodes)) {
// 支持哨兵模式
logger.info("RedissonClient init useSentinelServers");
String[] sentinels = sentinelNodes.split(",");
config.useSentinelServers()
.setMasterName(sentinelMaster)
.setDatabase(database)
.setPassword(StringUtils.isEmpty(password) ? null : password)
.setTimeout(timeout)
.addSentinelAddress(formatRedisUrls(sentinels))
;
} else if (host.contains(",")) {
// 集群模式
logger.info("RedissonClient init useClusterServers");
String[] clusterNodes = host.split(",");
ClusterServersConfig cConfig = config.useClusterServers();
for (String node : clusterNodes) {
cConfig.addNodeAddress("redis://" + node + ":" + port);
}
cConfig.setPassword(password);
cConfig.setMasterConnectionPoolSize(connectPoolSize);
cConfig.setMasterConnectionMinimumIdleSize(connectPoolIdleSize);
cConfig.setConnectTimeout(connectTimeout);
cConfig.setRetryAttempts(retryAttempts);
cConfig.setRetryInterval(retryInterval);
cConfig.setTimeout(timeout);
cConfig.setPingConnectionInterval(pingConnectionInterval);
} else {
// 单机模式
logger.info("RedissonClient init useSingleServer");
config.useSingleServer()
.setAddress("redis://" + host + ":" + port)
.setDatabase(database)
.setPassword(StringUtils.isEmpty(password) ? null : password)
.setConnectionPoolSize(connectPoolSize)
.setConnectionMinimumIdleSize(connectPoolIdleSize)
.setTimeout(timeout);
}
return Redisson.create(config);
}
/**
* 格式化 Redis URL,确保前缀一致
*/
private String[] formatRedisUrls(String[] nodes) {
return Arrays.stream(nodes)
.map(node -> node.startsWith("redis://") ? node : "redis://" + node)
.toArray(String[]::new);
}
}
连接成功
2.5 测试故障转移
你可以通过停掉主节点(6379
)来测试故障转移的功能。停掉主节点后,Sentinel 会自动将从节点提升为新的主节点。
停止主节点(
6379
)Sentinel 会检测到主节点宕机,并自动执行故障转移,将选择一个从节点升级为新的主节点。你可以通过以下命令查看新的 Sentinel 的主节点。可以看到新的主节点已经变成6381。
./src/redis-cli -p 26379 SENTINEL get-master-addr-by-name mymaster
1) "127.0.0.1"
2) "6381"
并且SpringBoot中自动感知到节点变化,业务可正常使用,不受到Redis节点宕掉的影响。
- 自动化:Sentinel 自动监控 Redis 状态,发生故障时,自动完成故障转移。
- 高可用:Redis Sentinel 可以防止单点故障,增强系统的稳定性。
⚡ 3. Redis Cluster:实现水平扩展和高可用性的终极武器
3.1 Redis Cluster的工作原理
Redis Cluster 是 Redis 3.0 引入的一个新特性,它可以实现水平扩展并且在多个节点之间分配数据。Redis Cluster 是基于分片的架构,数据会自动分布到不同的节点上。每个节点都有一个主节点和若干从节点组成的高可用集群。
当某个节点发生故障时,Redis Cluster 可以自动将从节点提升为主节点,继续保持服务的可用性。同时,Cluster 支持 自动分片,即每个节点负责一部分数据。
3.2 Redis Cluster的搭建步骤
持续更新中…
🚀 4. 实际应用案例:如何实现生产环境中的 Redis 高可用架构
在实际生产环境中,Redis 的高可用架构设计需要根据具体的需求来选择适合的方案。比如,对于需要高吞吐量和低延迟的场景,使用 Redis Cluster 是一个不错的选择;而对于一些中小型应用,可以选择 Redis Sentinel 来确保故障自动切换。
4.1 使用 Redis Sentinel 实现高可用
- 部署 Redis 实例:设置主从节点,并在至少 3 个机器上运行 Redis。
- 配置 Sentinel:配置 Sentinel 进行监控和故障转移。
- 自动切换:当主节点宕机时,Sentinel 自动将从节点提升为主节点。
4.2 使用 Redis Cluster 实现高可用
- 分配数据分片:将数据分配到多个 Redis 节点上,每个节点负责不同的数据分片。
- 设置集群:通过
redis-trib
工具来创建 Redis 集群。 - 监控集群状态:使用
redis-cli
检查集群状态,确保节点间通信正常。
🔒 5. 结论:如何选择合适的 Redis 高可用方案?
每个系统对 Redis 高可用性的需求都不尽相同,因此选择合适的架构尤为重要:
- 小型系统:可以选择 Redis 主从复制,设置少量的从节点来提升读取性能。
- 中型系统:使用 Redis Sentinel 来实现自动故障转移,并增加更多的监控和故障处理功能。
- 大型系统:对于大规模的应用,建议使用 Redis Cluster 进行分片存储与水平扩展,并结合 Sentinel 进行高可用性保障。
通过了解 Redis 的高可用架构,结合实际业务需求来进行部署,我们就能避免系统因为 Redis 故障导致的业务中断,提升整体系统的可靠性。
✨ 总结:Redis 缓存一直在线,稳定运行
Redis 的高可用性不仅关乎缓存系统的稳定性,更是支撑业务连续性的关键。通过选择合适的高可用架构,我们可以让 Redis 在系统故障时仍然坚挺不倒,保证业务流畅运行。
希望你通过这篇文章了解了 Redis 主从复制、Sentinel 和 Cluster 的不同高可用实现方式,找到适合自己的方案,确保 Redis 在高负载下仍能保持稳定运行!
🌟 你的支持是我持续创作的动力,欢迎点赞、收藏、分享!