什么是redis主从复制
当想master端写入数据时,通过redis sync机制将数据文件发送至slave端,确保slave端的数据和master数据一致。
主从复制的特点
1、一个master可以有多个slave
2、Master下的Slave还可以接受同一架构中其它slave的链接与同步请求,实现数据的级联复制,即Master->Slave->Slave模式
3、Master以非阻塞的方式同步数据至slave,这将意味着Master会继续处理一个或多个slave的读写请求(高并发的读写给master带来的压力很大)
4、Slave端同步数据也可以修改为非阻塞是的方式,当slave在执行新的同步时,它仍可以用旧的数据信息来提供查询;否则,当slave与master失去联系时,slave会返回一个错误给客户端;
5、主从复制具有可扩展性,即多个slave专门提供只读查询与数据的冗余,Master端专门提供写操作;
6、通过配置禁用Master数据持久化机制,将其数据持久化操作交给Slaves完成,避免在Master中要有独立的进程来完成此操作。
实现
在主机和虚拟机之间实现主从复制的简单实现。分别启动redis服务,主机作为master,虚拟机作为slave。在slave客户端下执行slaveof 192.168.1.106(master)6379
上面的方式只是保证了在执行slaveof命令之后,虚拟机上的slave成为master的从机,一旦服务重新启动之后,复制关系将终止。因此可以将这种配置关系放进配置文件里。
下面测试一下,在master下插入hello和world数据:
在从slave下查看:
神奇事情发生了,虚拟机上的slave居然读到了master的数据。以上就是redis是一个简单的主从复制实现。
原理
redis主从复制主要是两步:1、
同步操作
SYNC执行步骤:
1、从服务器向主服务器发送一个 SYNC 命令(初次连接或者重新连接都发送)。
2、接到 SYNC 命令的主服务器将执行BGSAVE ,在后台生成一个RDB文件(这个文件需要同步到从服务器)。
3、主服务器执行BGSAVE期间,所有执行的写入命令都将保存到一个缓冲区里面(不能保证在备份数据文件时没有用户操作,因此记录备份期间的写入操作)。
3、BGSAVE执行完毕后,主服务器将执行保存操作所得的 .rdb 文件发送给从服务器, 从服务器接收这个 .rdb 文件,并将文件中的数据载入到内存中。
4、然后主服务器会以 Redis 命令协议的格式,将缓冲区中积累的所有命令发送给从服务器(上面的第三步)。
ps:可以通过 telnet 命令来亲自验证这个同步过程: 首先连上一个正在处理命令请求的 Redis 服务器, 然后向它发送 SYNC 命令, 过一阵子, 你将看到 telnet 会话(session)接收到服务器发来的大段数据(.rdb 文件), 之后还会看到, 所有在服务器执行过的写命令, 都会重新发送到 telnet 会话来。
即使有多个从服务器同时向主服务器发送 SYNC , 主服务器也只需执行一次 BGSAVE 命令, 就可以处理所有这些从服务器的同步请求。
命令传播
由于主库在执行BGSAVE期间客户端依然可以进行读写操作,所以在复制同步rdb文件后主库的数据不一致,或者说在主从服务器断线重连之后执行同步动作时,生成完整的RDB文件并且发送到从服务器载入,但主从服务器的数据库状态在断线前基本上是一致的,不一致的部分只有断线后主服务器执行那一部分修改数据库的命令,如果这时执行一次SYNC命令就非常浪费,首先它毫无意义,其他是生成RDB文件是一个非常消耗CPU、内存和IO资源的过程,而且发送RDB文件到从服务器会占用大量的网络带宽资源,从服务器在载入RDB文件的过程中会阻塞不会响应任何命令,这对于从库是很不能忍受的所以大部分情况下执行SYNC命令是没有必要也是非常不合理的。
为了解决2.8之前版本SYNC命令的性能问题,2.8版本设计了一个新的命令PSYNC,PSYNC命令分为完整重同步 和 部分重同步 ,完整重同步过程用于从服务器初始化时初次复制的情况和SYNC命令基本一致,PSYNC则用于断线后重新复制,在条件允许的情况下,它不会生成RDB文件,而是给从服务器回复一个+Continue表示执行部分重同步,并且把从服务器断线后主服务器执行的修改数据库的命令发送到从服务器,从服务器执行这些命令同步数据库。
部分重同步功能由下面几个部分构成:
◆主服务器的复制偏移量 和 从服务器的复制偏移量 :当主服务器在向从服务器进行命令同步时,主服务器和从服务器会各自记录一个复制偏移量,当主从服务器的数据库状态一致时这两个复制偏移量是相同的,如果这两个偏移量不一致说明当前主从服务器的状态不一致。
◆主服务器的复制积压缓冲区 :复制积压缓冲区是一个固定大小的FIFO队列,当队列已满时会弹出最早插入的数据,在主服务器进行命令传播时会同时把命令放到缓冲区中,缓冲区包含两部分数据,偏移量和字节。在进行复制时从服务器会将偏移量上报到主服务器,主服务检查当前偏移量是否还存在缓冲区中,如果存在进行部分重同步,如果不存在进行完整重同步。因为这个积压缓冲区是一个固定大小的队列,所以当从服务器长时间断线时,从服务器的复制偏移量很可能已不再缓冲区中,这时候只能进行完整重同步。
◆服务器的运行ID :初次同步时主服务器会把ID发给从服务器,从服务器保存主服务器ID,当断线重连后,会把之前保存的主服务器ID上报给主服务器,主服务器检查从服务器之前复制的主服务器ID是否和自己的ID相同,如果相同,执行部分重同步,如果不同说明从服务器之前记录的状态不是当前主服务器,这时候需要执行完整重同步。
PSYNC命令实现
初始复制或者之前执行过SLAVEOF no one命令,执行完整重同步:发送PSYNC ? -1命令到主服务器。如果从服务器已经复制过某个主服务器,在开始新复制时向主服务器发送PSYNC <runid> <offset>命令,runid是上次复制的主服务器id,offset是从服务器的复制偏移量,主服务器会根据这个两个参数来决定做哪种同步,判断服务器id是否和本机相同,复制偏移量是否在缓冲区中,主服务器有三种回复:
▲回复+FULLRESYNC <runid> <offset>执行完整重同步,从服务器把offset当做初始复制偏移量
▲回复+CONTINUE,表示执行部分重同步,从服务器等待主服务器发送缺少的数据
▲回复-ERR,表示主服务器版本低于2.8,不支持PSYNC命令
新版本复制过程:
1、设置主服务器地址和端口,通过调用SAVEOF <master_ip> <master_port>命令。
2、建立套接字连接。
3、发送PING命令,检查主从服务器是否能够正常处理命令。
4、从服务器设置masterauth,主服务器设置requirepass进行身份验证。这两个选项要么都设置要么都不设置,如果只设置了一个从服务器向主服务器发送命令时会报错。
5、发送端口信息,通过执行命令REPLCONF listening-port <port-number>,向主服务器发送从服务器的监听端口号。
6、同步,从服务器向主服务器发送PSYNC命令。
7、命令传播,完成同步之后主服务器会把之后执行的写命令传播到从服务器保证主从服务器的状态一致。
心跳检测
在命令传播阶段,从服务器默认每秒一次的频率向主服务器发送命令:REPLCONF ACK <replication_offset>,replication_offset是从服务器的复制偏移量,该命令有三个作用:
◆检测从服务器的网络连接状态,检测主从服务器连接是否正常,如果主服务器超过一定时间没有收到从服务器的REPLCONF ACK 命令,那么它们的连接可能出了问题。
◆辅助实现min-slaves选项,min-slaves-to-write和min-slaves-max-lag两个选项可以防止主服务器在不安全的情况下执行写命令,min-slaves-to-write 3 min-slaves-max-lag 10 表示如果从服务器少于3个,或者3个从服务器的延迟都大于10秒时,主服务器拒绝写命令。
◆检测命令丢失,主服务器接收到从服务器的REPLCONF ACK 命令之后会检查从服务器的偏移量是否和主服务器的一致,如果不一致会把积压缓冲区中的从服务器偏移量后面的命令发送到从服务器。
关闭主服务器持久化时,复制功能的数据安全
当配置Redis复制功能时,强烈建议打开主服务器的持久化功能。 否则的话,由于延迟等问题,部署的服务应该要避免自动拉起。为了帮助理解主服务器关闭持久化时自动拉起的危险性,参考一下以下会导致主从服务器数据全部丢失的例子:
1、假设节点A为主服务器,并且关闭了持久化。 并且节点B和节点C从节点A复制数据
2、节点A崩溃,然后由自动拉起服务重启了节点A. 由于节点A的持久化被关闭了,所以重启之后没有任何数据
3、节点B和节点C将从节点A复制数据,但是A的数据是空的, 于是就把自身保存的数据副本删除。
在关闭主服务器上的持久化,并同时开启自动拉起进程的情况下,即便使用Sentinel来实现Redis的高可用性,也是非常危险的。 因为主服务器可能拉起得非常快,以至于Sentinel在配置的心跳时间间隔内没有检测到主服务器已被重启,然后还是会执行上面的数据丢失的流程。无论何时,数据安全都是极其重要的,所以应该禁止主服务器关闭持久化的同时自动拉起。
只读从服务器
从 Redis 2.6 开始, 从服务器支持只读模式, 并且该模式为从服务器的默认模式。
◆只读模式由 redis.conf 文件中的 slave-read-only 选项控制, 也可以通过 CONFIG SET 命令来开启或关闭这个模式。
◆只读从服务器会拒绝执行任何写命令, 所以不会出现因为操作失误而将数据不小心写入到了从服务器的情况。
◆即使从服务器是只读的, DEBUG 和 CONFIG 等管理式命令仍然是可以使用的, 还是不应该将服务器暴露给互联网或者任何不可信网络。 不过, 使用 redis.conf 中的命令改名选项, 可以通过禁止执行某些命令来提升只读从服务器的安全性。
一些不重要的临时数据, 仍然是可以保存在从服务器上面的。 比如说, 客户端可以在从服务器上保存主服务器的可达性信息, 从而实现故障转移策略。所以仍然要让一个从服务器变得可写。
总结
◆master最好不要做任何持久化工作,包括内存快照和AOF日志文件,特别是不要启用内存快照做持久化。
◆如果数据比较关键,某个Slave开启AOF备份数据,策略为每秒同步一次。
◆为了主从复制的速度和连接的稳定性,Slave和Master最好在同一个局域网内。
◆尽量避免在压力较大的主库上增加从库(这一点很重要,本来master压力就大,还要增加它的压力)
◆为了Master的稳定性,主从复制不要用图状结构,用单向链表结构更稳定,即主从关系为:Master<–Slave1<–Slave2<–Slave3…….,这样的结构也方便解决单点故障问题,实现Slave对Master的替换,也即,如果Master挂了,可以立即启用Slave1做Master,其他不变。
参考:
http://redisdoc.com/topic/replication.html
http://www.tuicool.com/articles/naUrMb