和Mysql主从复制的原因一样,Redis虽然读取写入的速度都特别快,但是也会产生读压力特别大的情况。为了分担读压力,Redis支持主从复制,Redis的主从结构可以采用一主多从或者级联结构,Redis主从复制可以根据是否是全量分为全量同步和增量同步。下图为级联结构。 

全量同步
Redis全量复制一般发生在Slave初始化阶段,这时Slave需要将Master上的所有数据都复制一份。具体步骤如下: 
–  从服务器连接主服务器,发送SYNC命令; 
–  主服务器接收到SYNC命名后,开始执行BGSAVE命令生成RDB文件并使用缓冲区记录此后执行的所有写命令; 
–  主服务器BGSAVE执行完后,向所有从服务器发送快照文件,并在发送期间继续记录被执行的写命令; 
–  从服务器收到快照文件后丢弃所有旧数据,载入收到的快照; 
–  主服务器快照发送完毕后开始向从服务器发送缓冲区中的写命令; 
–  从服务器完成对快照的载入,开始接收命令请求,并执行来自主服务器缓冲区的写命令;

增量同步
Redis增量复制是指Slave初始化后开始正常工作时主服务器发生的写操作同步到从服务器的过程。 
增量复制的过程主要是主服务器每执行一个写命令就会向从服务器发送相同的写命令,从服务器接收并执行收到的写命令。
 
Redis主从同步策略
主从刚刚连接的时候,进行全量同步;全同步结束后,进行增量同步。当然,如果有需要,slave 在任何时候都可以发起全量同步。redis 策略是,无论如何,首先会尝试进行增量同步,如不成功,要求从机进行全量同步。

注意点
如果多个Slave断线了,需要重启的时候,因为只要Slave启动,就会发送sync请求和主机全量同步,当多个同时出现的时候,可能会导致Master IO剧增宕机。

Redis主从复制的配置十分简单,它可以使从服务器是主服务器的完全拷贝。需要清楚Redis主从复制的几点重要内容:

1)Redis使用异步复制。但从Redis 2.8开始,从服务器会周期性的应答从复制流中处理的数据量。
2)一个主服务器可以有多个从服务器。
3)从服务器也可以接受其他从服务器的连接。除了多个从服务器连接到一个主服务器之外,多个从服务器也可以连接到一个从服务器上,形成一个图状结构。
4)Redis主从复制不阻塞主服务器端。也就是说当若干个从服务器在进行初始同步时,主服务器仍然可以处理请求。
5)主从复制也不阻塞从服务器端。当从服务器进行初始同步时,它使用旧版本的数据来应对查询请求,假设你在redis.conf配置文件是这么配置的。
否则的话,你可以配置当复制流关闭时让从服务器给客户端返回一个错误。但是,当初始同步完成后,需要删除旧的数据集和加载新的数据集,在
这个短暂的时间内,从服务器会阻塞连接进来的请求。
6)主从复制可以用来增强扩展性,使用多个从服务器来处理只读的请求(比如,繁重的排序操作可以放到从服务器去做),也可以简单的用来做数据冗余。
7)使用主从复制可以为主服务器免除把数据写入磁盘的消耗:在主服务器的redis.conf文件中配置“避免保存”(注释掉所有“保存“命令),然后连接一个配
置为“进行保存”的从服务器即可。但是这个配置要确保主服务器不会自动重启

只读从服务器

从Redis 2.6开始,从服务器支持只读模式,并且是默认模式。这个行为是由Redis.conf文件中的slave-read-only 参数控制的,可以在运行中通过CONFIG SET来启用或者禁用。

只读的从服务器会拒绝所有写命令,所以对从服务器不会有误写操作。但这不表示可以把从服务器实例暴露在危险的网络环境下,
因为像DEBUG或者CONFIG这样的管理命令还是可以运行的。不过你可以通过使用rename-command命令来为这些命令改名来增加安全性。

你可能想知道为什么只读限制还可以被还原,使得从服务器还可以进行写操作。虽然当主从服务器进行重新同步或者从服务器重启后,
这些写操作都会失效,还是有一些使用场景会想从服务器中写入临时数据的,但将来这个特性可能会被去掉。

限制有N个以上从服务器才允许写入

从Redis 2.8版本开始,可以配置主服务器连接N个以上从服务器才允许对主服务器进行写操作。但是,因为Redis使用的是异步主从复制,没办法确保从服务器确实收到了要写入的数据,所以还是有一定的数据丢失的可能性。

这一特性的工作原理如下:
1)从服务器每秒钟ping一次主服务器,确认处理的复制流数量。
2)主服务器记住每个从服务器最近一次ping的时间。
3)用户可以配置最少要有N个服务器有小于M秒的确认延迟。
4)如果有N个以上从服务器,并且确认延迟小于M秒,主服务器接受写操作。

还可以把这看做是CAP原则(一致性,可用性,分区容错性)不严格的一致性实现,虽然不能百分百确保一致性,但至少保证了丢失的数据不会超过M秒内的数据量。

如果条件不满足,主服务器会拒绝写操作并返回一个错误。
1)min-slaves-to-write(最小从服务器数)
2)min-slaves-max-lag(从服务器最大确认延迟)

了解了基本的原理,我们动手来做一个 一主二从 的主从复制吧

准备工作:

1、3台服务器,这边是3台虚拟机

虚拟机1: 192.168.65.100

虚拟机2:192.168.65.101

虚拟机3:192.168.65.102

2、全部安装了redis ,配置文件中,互相允许ip连接,并启动服务 ,用 info replication 查看一下 (如果是在一个机器下,可以复制多个配置文件,配置不同的名字,端口号,再启动,也是可以的,这里我们就以多服务器来配置)

127.0.0.1:6379> info replication
 Replication
 role:master
 connected_slaves:0
 master_failover_state:no-failover
 master_replid:8b0481a11d32cf7cbca7f1abd772addbbfd42430
 master_replid2:0000000000000000000000000000000000000000
 master_repl_offset:0
 second_repl_offset:-1
 repl_backlog_active:0
 repl_backlog_size:1048576
 repl_backlog_first_byte_offset:0
 repl_backlog_histlen:0

配置:我们一般配置从机就可以了 ,在配置文件中 replicaof 192.168.65.100 6379 这段配置项修改

//操作虚拟机2
 127.0.0.1:6379> info replication //查看信息
 Replication
 role:slave  //变成了从机
 master_host:192.168.65.100  //主机地址
 master_port:6379
 master_link_status:up
 master_last_io_seconds_ago:5
 master_sync_in_progress:0
 slave_repl_offset:14
 slave_priority:100
 slave_read_only:1
 connected_slaves:0
 master_failover_state:no-failover
 master_replid:b837ba76a2108efe9f10cd4bf8554f9e0157ce57
 master_replid2:0000000000000000000000000000000000000000
 master_repl_offset:14
 second_repl_offset:-1
 repl_backlog_active:1
 repl_backlog_size:1048576
 repl_backlog_first_byte_offset:1
 repl_backlog_histlen:14

//虚拟机3重复这样的操作
================================================
主机查看
127.0.0.1:6379> info replication
 Replication
 role:master
 connected_slaves:2 //2个
  //从机信息
 slave0:ip=192.168.65.101,port=6379,state=online,offset=280,lag=1 
 slave1:ip=192.168.65.102,port=6379,state=online,offset=294,lag=0
 master_failover_state:no-failover
 master_replid:b837ba76a2108efe9f10cd4bf8554f9e0157ce57
 master_replid2:0000000000000000000000000000000000000000
 master_repl_offset:294
 second_repl_offset:-1
 repl_backlog_active:1
 repl_backlog_size:1048576
 repl_backlog_first_byte_offset:1
 repl_backlog_histlen:294

我们来测试一下

//主服务器设置 key
127.0.0.1:6379> keys *
 (empty array)
 127.0.0.1:6379> set hello chris
 OK
 127.0.0.1:6379> get hello
 "chris"
================================从服务器:虚拟机2
127.0.0.1:6379> get hello  //成功读到数据
 "chris"
127.0.0.1:6379> set k1 k1  //由于默认从服务器只能读,所以写是不成功的.
 (error) READONLY You can't write against a read only replica.
=================================虚拟机3
127.0.0.1:6379> get hello
 "chris"

主从复制成功!!