Redis 高可用性:如何让你的缓存一直在线,稳定运行?

🎯 引言:Redis的高可用性为啥这么重要?

在现代高可用系统中,Redis 是一款不可或缺的分布式缓存与数据库系统。无论是提升访问速度,还是实现数据的高效持久化,Redis 都能轻松搞定。可是,当你把 Redis 用于关键业务时,它的高可用性就显得尤为重要。为了避免系统出现单点故障,保障业务连续性,我们需要有一套高可用架构来确保 Redis 的稳定运行。

今天,我们就一起来探讨一下 Redis 高可用架构 的几种实现方式,看看如何设计一套健壮的 Redis 高可用方案,保证我们的 Redis 缓存一直在线,稳定运行。


🛠️ 1. Redis 主从复制:简单的高可用方案

1.1 主从复制工作原理

Redis 主从复制(Master-Slave Replication)是 Redis 实现高可用性最基础的方案之一。在这种架构中,你会有一个主节点(Master)和一个或多个从节点(Slave)。主节点负责处理写操作和数据更新,而从节点则负责从主节点同步数据并进行只读操作。

当主节点发生故障时,可以将某个从节点提升为主节点(手动或自动),确保系统的持续可用。

主从复制基本架构

  • 主节点(Master) :处理所有的写操作(如SETDEL等命令)。
  • 从节点(Slave) :只处理读操作(如GET命令),并定期从主节点同步数据。

主从复制工作流程

(1)初始同步

当从节点(Slave)首次启动并连接到主节点时,它需要将主节点的数据完全同步过来。这个过程称为"全量同步"。

  1. 从节点连接主节点:从节点向主节点发起连接请求。
  2. 主节点进行快照(RDB) :主节点执行 SAVEBGSAVE 命令,将当前的内存数据(RDB 快照)保存到磁盘。
  3. 从节点复制快照:主节点将保存的数据发送给从节点,从节点将数据写入本地数据库。
  4. 建立同步通道:从节点在完成全量同步后,开始监听主节点的变更。
(2)增量同步

从节点已经有了主节点的数据快照后,接下来的数据同步就是增量同步,只同步主节点上的变更数据。

  1. 主节点记录变更操作 :主节点每处理一个写操作(如SETDEL),会将这个操作写入复制日志(replication log)中。
  2. 主节点发送变更命令给从节点:主节点将增量变更命令发送到所有的从节点。
  3. 从节点执行命令:从节点收到变更命令后,会在本地执行这些命令,保证数据的一致性。
(3)断线恢复

如果从节点与主节点的连接丢失,Redis 会进行断线恢复:

  • 当从节点重新连接到主节点时,Redis 会判断是进行全量同步 还是增量同步。如果从节点没有数据快照,它会进行全量同步;如果有数据快照,则会通过增量同步方式进行恢复。

主从复制的特点

  • 只读模式 :从节点默认只能进行读取操作(GET),不接受写操作。写操作必须通过主节点执行。
  • 延迟问题:由于从节点是从主节点获取数据的,主节点的写操作会有一定的延迟才会在从节点上看到。
  • 数据一致性:主从复制是最终一致性的,意味着从节点上的数据不会立即与主节点同步一致,在数据变更时可能会有短暂的不一致状态。

主从复制的优势

  • 提高可用性和可靠性:从节点可以用来分担主节点的读取压力,还可以在主节点故障时迅速切换,保证服务的可用性。
  • 数据备份:从节点可以作为主节点的备份,提供数据冗余,防止主节点数据丢失。
  • 负载均衡:多个从节点可以分担读取请求,从而降低主节点的负载。

主从复制使用场景

  • 高可用架构:当主节点出现故障时,可以通过从节点提升一个新的主节点来实现故障切换。
  • 读写分离:主节点处理写操作,从节点处理读操作,优化性能。
  • 数据冗余:从节点可以作为数据的备份,一旦主节点出现问题,可以快速恢复数据。

1.2 主从复制搭建步骤

1、Redis 环境准备

  1. 下载安装Redis

  2. 本次演示使用的版本是6.2.7版本(并且在本机环境模拟3个redis示例演示)

  3. 进行Redis目录下创建 redis-cluster (mkdir redis-cluster)

  4. 复制原有redis.conf新建3个配置文件 redis-6379.conf redis-6380.conf redis-6381.conf

  5. 将配置文件放置到redis-cluster目录下

  6. 编辑主节点(redis-6379.conf),主要修改以下配置项

    bash 复制代码
    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
  7. 编辑从节点(redis-6380.conf)

    bash 复制代码
    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
  8. 编辑从节点(redis-6381.conf)

    bash 复制代码
    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 实例

  1. 打开 命令终端

  2. 进入 Redis 所在的目录

  3. 启动3个实例

    bash 复制代码
    ./src/redis-server ./redis-cluster/redis-6379.conf
    ./src/redis-server ./redis-cluster/redis-6380.conf
    ./src/redis-server ./redis-cluster/redis-6381.conf
  4. 检查实例是否正常 你可以通过 redis cli 连接到每个实例,确认它们是否成功启动。

  5. 检查主从复制是否正常或者连接到主节点新增键值看同步是否成功

    bash 复制代码
    ./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 实例(例如637963806381)。
  • 你希望配置 Sentinel 来监控这些实例,并在主节点宕机时进行自动故障转移。

每个 Redis 哨兵都需要配置一个 sentinel.conf 文件来监控主节点。你可以创建多个 Sentinel 配置文件,并通过它们来监控 Redis 实例。

设置哨兵配置文件(sentinel.conf) 内容如下:

bash 复制代码
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 哨兵的配置。

bash 复制代码
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(哨兵)

  1. 你可以启动多个 Sentinel 实例来提高容错性。

    bash 复制代码
    ./src/redis-sentinel ./redis-cluster/sentinel-26379.conf
    ./src/redis-sentinel ./redis-cluster/sentinel-26380.conf
    ./src/redis-sentinel ./redis-cluster/sentinel-26381.conf
  2. 验证哨兵状态查看是否发现主节点

    bash 复制代码
    ./src/redis-cli -p 26379 sentinel masters
  3. 查看从节点状态

    bash 复制代码
    ./src/redis-cli -p 26379 sentinel slaves mymaster

    5. 搭建完成,这说明 Sentinel 已经自动发现并管理了从节点 🎯

2.4 哨兵模式连接Redis

在Sentinel集群监管下的Redis主从集群,其节点会因为自动故障转移而发生变化,Redis的客户端必须感知这种变化,及时更新连接信息。 客户端应用程序应该使用RedisSentinel模式连接Redis,而不是直接固定的Redis的IP。我们可以直接通过哨兵获取到当前的主节点IP。可以使用命令:

bash 复制代码
./src/redis-cli -p 26379 SENTINEL get-master-addr-by-name mymaster

我们通过一个测试来实现Spring Boot实现集成哨兵机制。

Maven依赖

xml 复制代码
        <dependency>
            <groupId>org.redisson</groupId>
            <artifactId>redisson-spring-boot-starter</artifactId>
            <version>3.15.3</version>
        </dependency>

配置文件添加配置项

yml 复制代码
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:

测试连接代码

java 复制代码
@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 会自动将从节点提升为新的主节点。

  1. 停止主节点(6379

  2. Sentinel 会检测到主节点宕机,并自动执行故障转移,将选择一个从节点升级为新的主节点。你可以通过以下命令查看新的 Sentinel 的主节点。可以看到新的主节点已经变成6381。

java 复制代码
	./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 实现高可用

  1. 部署 Redis 实例:设置主从节点,并在至少 3 个机器上运行 Redis。
  2. 配置 Sentinel:配置 Sentinel 进行监控和故障转移。
  3. 自动切换:当主节点宕机时,Sentinel 自动将从节点提升为主节点。

4.2 使用 Redis Cluster 实现高可用

  1. 分配数据分片:将数据分配到多个 Redis 节点上,每个节点负责不同的数据分片。
  2. 设置集群 :通过 redis-trib 工具来创建 Redis 集群。
  3. 监控集群状态 :使用 redis-cli 检查集群状态,确保节点间通信正常。

🔒 5. 结论:如何选择合适的 Redis 高可用方案?

每个系统对 Redis 高可用性的需求都不尽相同,因此选择合适的架构尤为重要:

  • 小型系统:可以选择 Redis 主从复制,设置少量的从节点来提升读取性能。
  • 中型系统:使用 Redis Sentinel 来实现自动故障转移,并增加更多的监控和故障处理功能。
  • 大型系统:对于大规模的应用,建议使用 Redis Cluster 进行分片存储与水平扩展,并结合 Sentinel 进行高可用性保障。

通过了解 Redis 的高可用架构,结合实际业务需求来进行部署,我们就能避免系统因为 Redis 故障导致的业务中断,提升整体系统的可靠性。


总结:Redis 缓存一直在线,稳定运行

Redis 的高可用性不仅关乎缓存系统的稳定性,更是支撑业务连续性的关键。通过选择合适的高可用架构,我们可以让 Redis 在系统故障时仍然坚挺不倒,保证业务流畅运行。

希望你通过这篇文章了解了 Redis 主从复制、Sentinel 和 Cluster 的不同高可用实现方式,找到适合自己的方案,确保 Redis 在高负载下仍能保持稳定运行!

🌟 你的支持是我持续创作的动力,欢迎点赞、收藏、分享!

相关推荐
hnlucky37 分钟前
redis 数据类型新手练习系列——Hash类型
数据库·redis·学习·哈希算法
AnsenZhu2 小时前
2025年Redis分片存储性能优化指南
数据库·redis·性能优化·分片
李菠菜3 小时前
非SpringBoot环境下Jedis集群操作Redis实战指南
java·redis
我的golang之路果然有问题4 小时前
快速了解redis,个人笔记
数据库·经验分享·redis·笔记·学习·缓存·内存
躺不平的理查德4 小时前
General Spark Operations(Spark 基础操作)
大数据·分布式·spark
talle20214 小时前
Zeppelin在spark环境导出dataframe
大数据·分布式·spark
渣渣盟4 小时前
大数据开发环境的安装,配置(Hadoop)
大数据·hadoop·分布式
道友老李5 小时前
【存储中间件】Redis核心技术与实战(五):Redis缓存使用问题(BigKey、数据倾斜、Redis脑裂、多级缓存)、互联网大厂中的Redis
redis·缓存·中间件
Angindem5 小时前
SpringClound 微服务分布式Nacos学习笔记
分布式·学习·微服务