高可用和高并发是互联网应用的基本要求,redis在早起版本提供了主从复制的方案给我们实现读写分离,但该方案若出现节点宕机,需要人工介入进行恢复,可用性并不高。哨兵模式在主从复制的基础上实现了自动故障转移,是一种更为自动的高可用方案。
伴随业务发展,用户数量增多,对并发要求越来越高,我们还希望服务能提供更高的QPS,更大的数据存储,集群模式应势而生。redis采用虚拟槽方案实现分片集群,以突破单机瓶颈获得更高的性能。
redis提供主从复制方案支持读写分离,以提高系统的读并发,当该方案不能保证集群的可用性,因此在实际生产环境并不推荐使用。但主从复制作为哨兵模式的基础,我们还是有必要了解其使用方式及工作原理。
1 主从架构集群搭建
开启主从复制模式的方式并不复杂,我们只需要修改配置文件并启动相对应的多个从节点实例,即可搭建一个主从复制架构的redis集群。本节我们以一主一从的方式,搭建一个主从架构集群。
为了便于管理,我们建议将集群配置文件放到同一的文件夹下进行管理。本文将在同台机器上启动6379(主节点)和6380(从节点)两个实例,配置文件存放位置规划如下:
.
└── conf
└── replication
├── 6379.conf
└── 6380.conf
1.1 启动主节点
主节点的启动和redis单机启动一致,只需要修改官方提供redis.conf配置文件,然后使用启动命令启动即可。reids主节点主要配置项如下:
#端口
port 6379
#后台运行
daemonize yes
#工作空间路径,RDB和AOF文件均会存放在该目录下
dir /data/redis/6379
#开启AOF持久化
appendonly yes
#配置fsync策略为每秒fsync一次
appendfsync everysec
#pid文件位置
pidfile /var/run/redis_6379.pid
#日志文件位置
logfile /var/logs/redis/redis_6379.log
使用启动命令启动主节点
redis-server conf/replication/6379.conf
1.2 启动从节点
redis主从复制架构从节点的启动也比较简单,只需要对原redis配置文件进行简单的修改即可。主要修改的配置项如下:
#端口
port 6380
#后台运行
daemonize yes
#工作空间路径,RDB和AOF文件均会存放在该目录下
dir /data/redis/6380
#pid文件位置
pidfile /var/run/redis_6380.pid
#日志文件位置
logfile /var/logs/redis/redis_6380.log
#指定主节点信息,从节点实例将从主节点复制数据
replicaof 192.168.0.60 6379
#设置本从节点只能进行读操作(建议此项设置为yes,设置允许写操作,仅限在数据生命周期很短的情况下使用,因为当从节点从主节点同步数据时,通过命令在从节点设置的数据将丢失,很容易出现数据不一致问题)
replica-read-only yes
注意如果主节点启用了密码认证,需要在配置文件中指定master的密码:
masterauth 主节点密码
然后使用启动命令指定配置文件,即可启动从节点
redis-server conf/replication/6380.conf
1.3 验证集群
至此,我们已经搭建好了一个主从架构的redis集群。我们使用redis-cli命令进入主节点,然后执行set操作,再到从节点查看是否有相同的数据以验证集群效果。
进入主节点设置数据:
root:# redis-cli -h localhost -p 6379
127.0.0.1:6379> set everlin 1
OK
127.0.0.1:6379> get everlin
"1"
进入从节点可以看到everlin这个key的值已经同步过来了:
root:# redis-cli -h localhost -p 6380
127.0.0.1:6379> get everlin
"1"
2 主从复制工作原理
2.1 sync全量复制(旧版本)
redis2.8版本之前,主从复制是通过sync命令进行全量复制的。当从节点启动时,其会建立一个连接到主节点,并发送sync命令从主节点同步数据。主节点在收到sync命令后,会使用bgsave命令在后台异步生成当前内存中的RDB快照。在此期间,主节点依旧可对外提供正常服务,期间的写操作会记录到缓存中。当RDB快照生成完成之后,主节点会将RDB文件通过网络传输给从节点。从节点会将数据暂时存放到一个临时文件中,当RDB快照接收完成后,再将其覆盖到从节点的RDB路径,并通过rdbLoad方法将数据载入内存中,完成之后会将节点的repl_state设置为REDIS_REPL_CONNECTED并进入命令传播模式,主节点会将bgsave之后在主节点上执行的命令传输给从节点,以保证从节点数据与主节点数据的一致性。
时间点 | 主节点状态 | 从节点状态 |
---|---|---|
t0 | 发起sync命令 | |
t1 | 使用bgsave命令异步生成RDB快照 | 等待同步 |
t2 | 等待同步 | |
t3 | 收到客户端写命令1 | 等待同步 |
t4 | 收到客户端写命令2 | 等待同步 |
t5 | RDB快照生成完成 | 等待同步 |
t6 | 向从节点发送RDB快照大小 | 准备接收RDB文件 |
t7 | 向从节点发送RDB快照 | 接收RDB并写入临时文件 |
t8 | RDB文件发送完成 | 将临时文件覆盖到RDB路径,并加载RDB |
t9 | 加载RDB成功,将repl_state设置为REDIS_REPL_CONNECTED,进入命令传播模式 | |
t10 | 发送缓存区中的写命令 | 执行写命令 |
注意:
【1】若需要slave节点在同步期间停止对外服务(保证数据一致性),可关闭slave配置文件中的replica-serve-stale-data即可。
# 默认为yes
replica-serve-stale-data no
2.2 psync增量同步(新版本)
在redis2.8版本之前,每次slave与master连接后,包括slave断线重连之后,都是使用sync进行全量同步。网络是个很复杂的环境,因网络波动造成连接断开是很常见的事情。而redis在每次断线之后,都仍采用全量同步的方式进行数据同步,效率十分低下。在2.8版本之后,redis开始支持slave断线重连后采用增量同步的方式进行数据复制,大大降低了网络开销,也提高了redis的性能。
slave初次与master连接时,psync与sync一样会通过bgsave在master生成RDB快照并进行全量同步,区别在于全量同步完成之后,slave会保存一个offset值记录当前数据的偏移量,并在发生断线之后,将该偏移量给到master,master将根据情况开启增量同步。而master会将客户端操作的命令记录在repl_backlog(缓冲区)中,并记录该backlog的数据偏移值repl_backlog_off,当slave发起psync时,master会判断slave传来的offset是否在backlog中,若存在则开启增量同步,若不存在则使用全量同步。
整体流程大致如下:
时间点 | 主节点状态 | 从节点状态 |
---|---|---|
tn+0 | repl_backlog_off=500,repl_backlog_histlen=500(注:repl_backlog_off到repl_backlog_off+repl_backlog_histlen为master缓冲区中命令的偏移范围) | 全量同步完成,offset=1000 |
tn+1 | 客户端操作,repl_backlog_histlen=501 | 命令传播:同步master操作,repl_backlog_off=1001 |
tn+2 | 连接断开,尝试重连 | |
tn+3 | 客户端操作,repl_backlog_histlen=502 | 连接断开,尝试重连 |
tn+4 | 连接断开,尝试重连 | |
tn+5 | 重连成功,发送命令:psync(offset=1001) | |
tn+6 | offset=1001在缓冲区中,开启增量同步 | |
tn+7 | 将offset之后的命令,以RESP协议格式同步给slave | 接收命令并执行 |
tn+8 | 完成同步,进入命令传播模式 |