1. 主从复制简介
Redis主从复制(Master-Slave Replication)是Redis提供的一种数据冗余技术,允许多个Redis服务器(从节点)拥有主节点数据的精确副本。通过主从复制,Redis实现了数据的备份、负载均衡和高可用性,是构建Redis高可用架构的基础。
主从复制的基本原理是将一台Redis服务器作为主节点(Master),其余的作为从节点(Slave/Replica)。主节点负责处理写操作,从节点通过复制主节点的数据来保持数据的一致性,并可以处理读操作,从而分担主节点的负载。
2. 主从复制的作用
Redis主从复制主要有以下几个作用:
- 数据备份:通过复制,可以避免单点故障导致的数据丢失
- 读写分离:主节点处理写操作,从节点处理读操作,提高系统整体性能
- 负载均衡:将读请求分散到多个从节点,减轻主节点压力
- 高可用基础:为Redis Sentinel和Redis Cluster等高可用方案提供基础支持
- 数据分析:可以在从节点上进行数据分析等密集型计算,不影响主节点性能
3. 主从复制的工作原理
3.1 复制的三个阶段
Redis的主从复制过程可以分为三个阶段:连接建立阶段、数据同步阶段和命令传播阶段。
3.1.1 连接建立阶段
- 从节点向主节点发送
SLAVEOF
(Redis 5.0后改为REPLICAOF
)命令,请求成为主节点的从节点 - 主节点收到命令后,记录从节点的信息
- 从节点和主节点建立连接,准备进行数据同步
3.1.2 数据同步阶段
Redis 2.8之前使用同步(sync)命令进行数据同步,2.8之后引入了更高效的部分重同步(psync)机制。数据同步有两种方式:
全量同步(Full Resynchronization):
- 主节点执行
BGSAVE
命令生成RDB文件 - 主节点将RDB文件发送给从节点
- 从节点清空自己的数据,加载RDB文件
- 主节点将缓冲区中的写命令发送给从节点
- 主节点执行
部分重同步(Partial Resynchronization):
- 主节点维护一个复制积压缓冲区(replication backlog)
- 当从节点断线重连时,从节点发送之前的复制偏移量
- 如果偏移量在缓冲区范围内,主节点只发送断线期间的写命令
- 否则执行全量同步
3.1.3 命令传播阶段
当主从节点完成数据同步后,主节点会将自己执行的写命令实时发送给从节点,从节点接收并执行这些命令,保持与主节点数据的一致性。
3.2 复制的底层实现
Redis主从复制的底层实现涉及以下关键机制:
- 复制偏移量:主从节点各自维护一个复制偏移量,用于记录复制的进度
- 复制积压缓冲区:主节点维护的一个固定长度的先进先出队列,默认大小为1MB
- 服务器运行ID:每个Redis实例都有一个唯一的运行ID,用于识别实例
- PSYNC命令:Redis 2.8引入的部分重同步命令,格式为
PSYNC <runid> <offset>
4. 主从复制的配置方式
4.1 命令行配置
可以通过在从节点执行以下命令,将其配置为主节点的从节点:
SLAVEOF <master-ip> <master-port> # Redis 5.0之前
REPLICAOF <master-ip> <master-port> # Redis 5.0及之后
要取消复制,恢复为独立节点,可以执行:
SLAVEOF NO ONE # Redis 5.0之前
REPLICAOF NO ONE # Redis 5.0及之后
4.2 配置文件配置
在从节点的redis.conf文件中添加以下配置:
# Redis 5.0之前
slaveof <master-ip> <master-port>
# Redis 5.0及之后
replicaof <master-ip> <master-port>
# 如果主节点设置了密码,需要配置主节点密码
masterauth <master-password>
4.3 其他重要配置参数
# 从节点是否只读(默认yes)
replica-read-only yes
# 主节点是否需要密码验证
masterauth <password>
# 复制积压缓冲区大小(默认1MB)
repl-backlog-size 1mb
# 复制积压缓冲区存活时间(默认3600秒)
repl-backlog-ttl 3600
# 主节点在没有从节点的情况下是否停止写入(默认no)
min-replicas-to-write 0
min-replicas-max-lag 10
5. 主从复制的数据一致性
5.1 主从复制的延迟问题
由于Redis主从复制是异步的,主节点不会等待从节点确认就返回客户端结果,因此会存在一定的复制延迟。这可能导致以下问题:
- 数据不一致:从节点的数据可能落后于主节点
- 数据丢失:如果主节点在数据还未同步到从节点时故障,数据可能丢失
5.2 减少复制延迟的方法
- 合理配置网络:确保主从节点间网络带宽充足,延迟低
- 调整复制缓冲区大小:增加
repl-backlog-size
参数值 - 使用更快的硬件:特别是磁盘和网络设备
- 优化写入模式:避免大量集中写入,分散写操作
- 监控复制延迟:通过
INFO replication
命令监控lag
值
6. 主从复制的高级特性
6.1 级联复制
Redis支持级联复制(Cascading Replication),即从节点可以作为其他从节点的主节点。这种结构可以减轻主节点的复制压力,但会增加数据一致性的延迟。
A (Master) --> B (Slave/Master) --> C (Slave)
6.2 复制过滤器(Redis 7.0新特性)
Redis 7.0引入了复制过滤器(Replication Filters),允许从节点选择性地复制主节点的数据,可以通过以下配置实现:
replica-ignore-keys-filtering <pattern1> <pattern2> ...
6.3 无盘复制
默认情况下,主节点在进行全量同步时会先将数据保存为RDB文件,然后再发送给从节点。无盘复制(Diskless Replication)可以直接将RDB数据通过网络发送给从节点,不需要写入磁盘:
repl-diskless-sync yes
repl-diskless-sync-delay 5
7. 主从复制的监控与维护
7.1 监控复制状态
可以通过INFO replication
命令查看主从复制的状态:
127.0.0.1:6379> INFO replication
# Replication
role:master
connected_slaves:1
slave0:ip=127.0.0.1,port=6380,state=online,offset=42,lag=0
master_replid:8371b4fb1155b71f4a04d3e1bc3e98a5a9104246
master_replid2:0000000000000000000000000000000000000000
master_repl_offset:42
second_repl_offset:-1
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:1
repl_backlog_histlen:42
7.2 常见问题及解决方案
复制中断:
- 检查网络连接
- 增加复制超时时间:
repl-timeout
- 检查防火墙设置
全量同步频繁:
- 增加复制积压缓冲区大小:
repl-backlog-size
- 检查网络稳定性
- 避免频繁重启从节点
- 增加复制积压缓冲区大小:
复制延迟过大:
- 检查主从节点负载
- 优化网络环境
- 考虑使用级联复制分担压力
8. 主从复制的实际应用案例
8.1 基本主从架构搭建
以下是一个简单的一主两从架构的配置示例:
主节点配置(redis-6379.conf):
port 6379
daemonize yes
pidfile /var/run/redis_6379.pid
logfile "6379.log"
dbfilename "dump-6379.rdb"
dir ./
从节点1配置(redis-6380.conf):
port 6380
daemonize yes
pidfile /var/run/redis_6380.pid
logfile "6380.log"
dbfilename "dump-6380.rdb"
dir ./
replicaof 127.0.0.1 6379
从节点2配置(redis-6381.conf):
port 6381
daemonize yes
pidfile /var/run/redis_6381.pid
logfile "6381.log"
dbfilename "dump-6381.rdb"
dir ./
replicaof 127.0.0.1 6379
启动命令:
redis-server redis-6379.conf
redis-server redis-6380.conf
redis-server redis-6381.conf
8.2 Java客户端连接示例
使用Jedis客户端连接主从架构:
// 连接主节点(写操作)
Jedis masterJedis = new Jedis("127.0.0.1", 6379);
masterJedis.set("key", "value");
// 连接从节点(读操作)
Jedis slaveJedis = new Jedis("127.0.0.1", 6380);
String value = slaveJedis.get("key");
System.out.println(value);
使用Lettuce客户端实现读写分离:
// 创建主节点连接
RedisClient masterClient = RedisClient.create("redis://127.0.0.1:6379");
StatefulRedisConnection<String, String> masterConnection = masterClient.connect();
RedisCommands<String, String> masterCommands = masterConnection.sync();
// 创建从节点连接池
List<RedisURI> slaveUris = Arrays.asList(
RedisURI.create("redis://127.0.0.1:6380"),
RedisURI.create("redis://127.0.0.1:6381")
);
RedisClusterClient slaveClient = RedisClusterClient.create(slaveUris);
StatefulRedisClusterConnection<String, String> slaveConnection = slaveClient.connect();
RedisAdvancedClusterCommands<String, String> slaveCommands = slaveConnection.sync();
// 写操作使用主节点
masterCommands.set("key", "value");
// 读操作使用从节点
String value = slaveCommands.get("key");
System.out.println(value);
9. 主从复制与其他高可用方案的结合
9.1 主从复制 + Sentinel
Redis Sentinel建立在主从复制的基础上,通过监控主从节点,在主节点故障时自动进行故障转移。这是Redis官方推荐的高可用方案。
9.2 主从复制 + Redis Cluster
Redis Cluster也使用主从复制来保证每个数据分片的高可用性。每个分片包含一个主节点和多个从节点,当分片的主节点故障时,会从其从节点中选举新的主节点。
10. 总结
10.1 主从复制的优缺点
优点:
- 提高了系统的读性能和可用性
- 实现了数据的热备份
- 为高可用方案提供基础支持
缺点:
- 不保证强一致性,存在复制延迟
- 主节点故障时需要手动切换(如果不结合Sentinel或Cluster)
- 写性能受限于单个主节点