Redis目前支持复制,不支持集群(在开发中)。Redis复制流程概述:Redis的复制功能是完全建立在之前我们讨论过的基于内存快照的持久化策略基础上的,也就是说无论你的持久化策略选择的是什么,只要用到了Redis的复制功能,就一定会有内存快照发生,即使主没有开启了snapshot持久,也会有rbd文件生成(原因下面说明)。那么首先要注意你的系统内存容量规划。
Redis复制流程在Slave和Master端各自是一套状态机流转,涉及的状态信息是:
整个流程过程如下:
Slave端在配置文件中添加了slave of指令,于是Slave启动时读取配置文件,初始状态为REDIS_REPL_CONNECT。
Slave端在定时任务serverCron(Redis内部的定时器触发事件)中连接Master,发送sync命令,然后阻塞等待master发送回其内存快照文件(最新版的Redis已经不需要让
Slave阻塞)。 Master端收到sync命令简单判断是否有正在进行的内存快照子进程,没有则立即开始内存快照,有则等待其结束,当快照完成后会将该文件发送给Slave端。
Slave端接收Master发来的内存快照文件,保存到本地,待接收完成后,清空内存表,重新读取Master发来的内存快照文件,重建整个内存表数据结构,并最终状态置位为
REDIS_REPL_CONNECTED状态,Slave状态机流转完成。 Master端在发送快照文件过程中,接收的任何会改变数据集的命令都会暂时先保存在Slave网络连接的发送缓存队列里(list数据结构),待快照完成后,依次发给Slave,之后
收到的命令相同处理,并将状态置位为 REDIS_REPL_ONLINE。
整个复制过程完成,流程如下图所示:
Redis复制机制的缺陷:
从上面的流程可以看出,Slave从库在连接Master主库时,Master会进行内存快照,然后把整个快照文件发给Slave,也就是没有象MySQL那样有复制位置的概念,即无增量复制,这会给整个集群搭建带来非常多的问题。
比如一台线上正在运行的Master主库配置了一台从库进行简单读写分离,这时Slave由于网络或者其它原因与Master断开了连接,那么当Slave进行重新连接时,需要重新获取整个Master的内存快照,Slave所有数据跟着全部清除,然后重新建立整个内存表,一方面Slave恢复的时间会非常慢,另一方面也会给主库带来压力。
所以基于上述原因,如果你的Redis集群需要主从复制,那么最好事先配置好所有的从库,避免中途再去增加从库。
注意:
Redis的复制功能没有增量复制,每次重连都会把主库整个内存快照发给从库,所以需要避免向在线服务的压力较大的主库上增加从库。
Redis的复制由于会使用快照持久化方式,所以如果你的Redis持久化方式选择的是日志追加方式(aof),那么系统有可能在同一时刻既做aof日志文件的同步刷写磁盘,又做快照写磁盘操作,这个时候Redis的响应能力会受到影响。所以如果选用aof持久化,则加从库需要更加谨慎。
以上来自Redis复制与可扩展集群搭建中的复制介绍。
按照 NoSQL之【Redis】学习(一):安装说明 这篇文章装好Redis。配置从Redis,很简单,只需要修改配置文件(redis_slave.conf)的几个参数:
#Pid 文件
pidfile /var/run/redis2.pid#端口
port 6380
#套接字文件
unixsocket /tmp/redis2.sock
#日志文件
logfile /var/log/redis/redis2.log
#持久化文件目录
dir /var/lib/redis2
#从配置选项# slaveof <masterip> <masterport>
slaveof 192.168.200.202 6379
指定配置文件启动后(/usr/local/bin/redis-server /etc/redis_slave.conf),日志信息如下:
1 [22108] 27 May 11:53:00.495 * MASTER <-> SLAVE sync started
2 [22108] 27 May 11:53:00.498 * Non blocking connect for SYNC fired the event.
3 [22108] 27 May 11:53:00.498 * Master replied to PING, replication can continue...
4 [22108] 27 May 11:53:00.563 * MASTER <-> SLAVE sync: receiving 18 bytes from master
5 [22108] 27 May 11:53:00.563 * MASTER <-> SLAVE sync: Loading DB in memory
6 [22108] 27 May 11:53:00.563 * MASTER <-> SLAVE sync: Finished with success
从Redis已经完成,测试replication:
主:192.168.200.25 6379
从:192.168.200.202 6379
#测试前检查MS一致性:
root@zhoujy:~# redis -h 192.168.200.25
redis 192.168.200.25:6379> keys *
(empty list or set)
redis 192.168.200.25:6379>
root@zhoujy:~# redis -h 192.168.200.202
redis 192.168.200.202:6379> keys *
(empty list or set)
redis 192.168.200.202:6379>
#测试同步
root@zhoujy:~# redis -h 192.168.200.25
redis 192.168.200.25:6379> set name zhoujinyi
OK
redis 192.168.200.25:6379> set sex man
OK
redis 192.168.200.25:6379> set age 27
OK
redis 192.168.200.25:6379> save
OK
redis 192.168.200.25:6379> keys *
1) "name"
2) "age"
3) "sex"
redis 192.168.200.25:6379>
root@zhoujy:~# redis -h 192.168.200.202
redis 192.168.200.202:6379> keys *
1) "age"
2) "sex"
3) "name"
redis 192.168.200.202:6379>
上面结果显示,数据已经从M同步到了S。
注意:创建复制时,可以很清楚的看到Master上仅在第一次做Slave同步时创建了dump.rdb文件,之后就通过增量传输命令的方式给Slave了,rdb文件没有再增大。而Slave的RDB和AOF 随着数据的增加而增大,并且AOF的增长速度大于RDB。
在上一篇文章NoSQL之【Redis】学习(三):Redis持久化 Snapshot和AOF说明 里面已经说了:
利用Replication机制来弥补aof、snapshot性能上的不足,达到了数据可持久化。即Master上Snapshot和AOF都不做,来保证Master的读写性能,而Slave上则同时开启Snapshot
和AOF来进行持久化,保证数据的安全性
既然Master上持久化不做,只在Slave上做持久化,那Master崩溃数据就会全部丢失,那如何恢复呢?接下来进行测试:
Master的配置:关闭自动持久化
#save 900 1
#save 300 10
#save 60 10000
appendonly no
Slave的配置:开启自动持久化
save 900 1
save 300 10
save 60 10000
appendonly yes
appendfilename appendonly.aof
appendfsync everysec
no-appendfsync-on-rewrite yes
auto-aof-rewrite-percentage 100
auto-aof-rewrite-min-size 64mb
这些配置的意思请看NoSQL之【Redis】学习(二):配置说明 ,重启主从服务器,使配置生效。
测试:
# 检查配置是否生效
redis 192.168.200.25:6379> keys *
(empty list or set)
redis 192.168.200.25:6379> CONFIG GET save
1) "save"
2) ""
redis 192.168.200.25:6379> CONFIG GET append*
1) "appendonly"
2) "no"
redis 192.168.200.202:6379> CONFIG GET save
1) "save"
2) "900 1 300 10 60 10000"
redis 192.168.200.202:6379> CONFIG GET append*
1) "appendonly"
2) "yes"
3) "appendfsync"
4) "everysec"
#检查同步情况
root@zhoujy:~# redis -h 192.168.200.25
redis 192.168.200.25:6379> keys *
(empty list or set)
redis 192.168.200.25:6379>
root@zhoujy:~# redis -h 192.168.200.202
redis 192.168.200.202:6379> keys *
(empty list or set)
redis 192.168.200.202:6379>
root@zhoujy:~# redis -h 192.168.200.25
redis 192.168.200.25:6379> set name zhoujinyi
OK
redis 192.168.200.25:6379> set sex man
OK
redis 192.168.200.25:6379> set age 27
OK
redis 192.168.200.25:6379> keys *
1) "age"
2) "name"
3) "sex"
redis 192.168.200.25:6379>
root@zhoujy:~# redis -h 192.168.200.202
redis 192.168.200.202:6379> keys *
1) "name"
2) "age"
3) "sex"
#以上测试已经证明同步成功
#M崩溃恢复:
redis 192.168.200.25:6379> INFO #通过INFO命令查看M的信息
# Server
……
# Clients
……
# Memory
……
# Persistence
rdb_changes_since_last_save:3 #离最后一次保存改变了3个key
aof_enabled:0 #关闭了aof持久
# Stats
……
# Replication
role:master #该实例为主服务器
connected_slaves:1 #有一个从服务器连接
slave0:192.168.200.202,6379,online #从服务器的信息
# CPU
……
# Keyspace
db0:keys=3,expires=0 #该实例有3个key
#模拟主库异常崩溃root@zhoujy:~# kill -9 1647
root@zhoujy:~# /etc/init.d/redis-server start #崩溃后启动
Starting redis-server: redis-server.
root@zhoujy:~# redis -h 192.168.200.25
#M崩溃恢复:
redis 192.168.200.25:6379> INFO #通过INFO命令查看M的信息
# Server
……
# Clients
……
# Memory
……
# Persistence
rdb_changes_since_last_save:0 #离最后一次保存改变了0个key
aof_enabled:0 #关闭了aof持久
# Stats
……
# Replication
role:master
connected_slaves:1
slave0:192.168.200.202,6379,online #和之前信息一样,那是不是数据也正常呢?
# CPU
……
# Keyspace #没有key信息,数据丢失。---不正常,丢失了
redis 192.168.200.25:6379> keys *
(empty list or set) #确实M上已经没有数据
redis 192.168.200.25:6379>
root@zhoujy:~# redis -h 192.168.200.202 #进入从库看看是否正常
redis 192.168.200.202:6379> keys *
(empty list or set) #S上也没有数据了?Why?
Slave上数据也随着丢失的原因是上面提到过的一个复制的缺陷引起的:
Slave由于网络或者其它原因与Master断开了连接,那么当Slave进行重新连接时,需要重新获取整个Master的内存快照,Slave所有数据跟着全部清除,然后重新建立整个内存表。上面的情况是因为这时Master已经丢失了数据,快照是空的,直接通过空的快照传给Slave,导致Slave的数据也被清空了。那如何避免呢?
继续测试:
# 从上面的 “模拟异常崩溃” 开始测试
#模拟异常崩溃
root@zhoujy:/var/lib/redis# /etc/init.d/redis-server stop #关闭主Redis
Stopping redis-server: redis-server.
root@zhoujy:~# redis -h 192.168.200.202 #查看slave的状态
redis 192.168.200.202:6379> INFO
# Server
……
# Clients
……
# Memory
……
# Persistence
……
# Stats
……
# Replication
role:slave
master_host:192.168.200.25
master_port:6379
master_link_status:down # Master已经不可访问了(down)。Slave依然运行良好,并且保留有AOF与RDB文件。
master_last_io_seconds_ago:-1
master_sync_in_progress:0
master_link_down_since_seconds:160
slave_priority:100
slave_read_only:1
connected_slaves:0
# CPU
……
# Keyspace
db0:keys=3,expires=0 #看到从上还保留3个key
#我们将通过Slave上保存好的AOF与RDB文件来恢复Master上的数据。把Slave上的RDB文件复制到主上。(保证文件名和配置文件一致)
1,首先,将Slave上的同步状态取消,避免主库在未完成数据恢复前就重启,进而直接覆盖掉从库上的数据,导致所有的数据丢失(上面测试出现的情况)
redis 192.168.200.202:6379> CONFIG GET slaveof #查看同步配置1) "slaveof"
2) "192.168.200.25 6379"
redis 192.168.200.202:6379> SLAVEOF NO ONE #断开同步
OK
redis 192.168.200.202:6379> INFO #断开同步成功
# Replication
role:master
connected_slaves:0
2,然后,把RDB文件复制到指定地方:
cp dump.rdb /var/lib/redis/zhoujy.rdb #注意相关权限(redis)
root@zhoujy:/var/lib/redis# /etc/init.d/redis-server start #启动主Redis
Starting redis-server: redis-server.
查看下日志里面的记录:“* DB loaded from disk: 0.000 seconds”,主已经从RDB文件里面恢复。进入查看下:
redis 192.168.200.25:6379> INFO# Keyspace
db0:keys=3,expires=0 #主恢复了3个kye
redis 192.168.200.25:6379> keys * #数据已恢复
1) "sex"
2) "age"
3) "name
3,最后,重新开启主从:
redis 192.168.200.202:6379> SLAVEOF 192.168.200.25 6379 #从重新把主的内存快照取到,清空本地内存,重放取到的内存快照。
OKredis 192.168.200.202:6379> INFO
# Replication
role:slave
master_host:192.168.200.25
master_port:6379
master_link_status:up
master_last_io_seconds_ago:1
master_sync_in_progress:0
slave_priority:100
slave_read_only:1
connected_slaves:0
通过此次实验,我们通过在Slave上启的快照,来保证了数据,恢复了Mater。根据上篇持久性介绍的文章,在大数据量的前提下,AOF的增长要比RDB快很多。所以要是对数据的一致性要求不是很高,允许丢失一些数据的话,建议只在从上开启Snapshot来进行持久化,不开启AOF(AOF会阻塞读写?)。同时可以考虑将save中的频率调高一些或者调用一个计划任务来进行定期bgsave的快照存储,来尽可能的保障本地化数据的完整性。样的架构下,如果仅仅是Master挂掉,Slave完整,数据恢复可达到100%;如果Master与Slave同时挂掉的话,数据的恢复也可以达到一个可接受的程度。当然不允许数据丢失的话,就要开启AOF了,不过最好新建的时候就开启,不要中途开启,上篇有说明。
总结:
因为在Redis中,持久化和复制的重连都需要一定的开销,极端情况下这个开销会导致数据库的不可用,所以根据自身的情况,建立合适的主从和持久化方案,可以让Redis和系统更稳定的运行。