Redis学习二:Redis高并发之主从模式

申明

本文章首发自本人公众号:壹枝花算不算浪漫,如若转载请标明来源!

感兴趣的小伙伴可关注个人公众号:壹枝花算不算浪漫

22.jpg22.jpg

前言

前面已经学习了Redis的持久化方式,接下来开始学习Redis主从架构的原理,来看看Redis如何利用主从架构来保证高并发的。

Redis如何支持高并发

单机的redis一般QPS不会超过超过10万+,一般单机QPS都在几万左右,如果需要支撑高并发,我们可以将Redis做成主从架构来支持读写分离。

主从架构 -> 读写分离 -> 支撑10万+读QPS

主从架构的核心原理

当启动一个slave node的时候,它会发送一个PSYNC命令给master node

如果这是salve node重复你给你连接master node,那么master node仅仅会复制给slave部分缺失的数据;否则如果是slave node第一次连接master node,那么会触发一次full resynchronization

开始full resynchronization的时候,master会启动一个后台线程,开始生成一份RDB快照文件,同时还会将从客户端收到的所有写命令缓存在内存中。

RDB文件生成完毕之后,master将这个RDB发送给slave,salve会先写入本地磁盘,然后再从本地磁盘加载到内存中。

接着master会将内存中缓存的写命令发送给slave,slave也会同步这些数据。

主从复制断点续传

slave node如果跟master node有网络故障,断开了连接,会自动重连。

从redis 2.8之后,就支持主从复制的断点续传,如果主从复制过程中,网络连接断掉了,那么可以接着上次复制的地方,继续复制下去,而不是从头开始复制一份

master node会在内存中创建一个backlog,master和slave都会保存一个replica offset还有一个master id,offset就是保存在backlog中的。如果master和slave网络连接断掉了,slave会让master从上次的replica offseet开始继续复制

但是如果没有找到对应的offset,那么就会执行一次full resynchronization

无磁盘化复制

master在内存中直接创建rdb,然后发送给slave,不会在自己的本地落地磁盘了

// 默认不使用diskless同步方式,可以改成yes
repl-diskless-sync yes
// 无磁盘diskless方式在进行数据传递之前会有一个时间的延迟,以便slave端能够进行到待传送的目标队列中,这个时间默认是5秒
repl-diskless-sync-delay 5

过期key处理

slave不会过期key,只会等待master过期key。如果master过期了一个key,或者通过LRU淘汰了一个key,那么会模拟一条del命令发送个slave。

以上的执行流程如图:

image.jpgimage.jpg

复制的完整流程

  1. slave node在redis.conf中的slaveof配置master的host信息,slave node启动,仅仅是保存了master node信息,此时复制流程并未开始
  2. slave node内部有个定时任务,每秒检查是否有新的master node需要连接和复制,如果发现,就跟master node建立socket网络连接
  3. slave node发送ping的命令给master node
  4. 口令认证,如果master设置了requiresspass,那么slave node必须发送masterauth的口令过去认证
  5. master node第一次执行全量复制,将所有数据发送给slave node
  6. master node后续持续将写命令,异步复制给slave node

具体流程如下:(以下内容参考自:
https://blog.csdn.net/houjixin/article/details/27680183)

全量复制

全备份过程中,在slave启动时,会向其master发送一条SYNC消息,master收到slave的这条消息之后,将可能启动后台进程进行备份,备份完成之后就将备份的数据发送给slave,初始时的全同步机制是这样的:

  1. slave启动后向master发送同步指令SYNC,master接收到SYNC指令之后将调用该命令的处理函数syncCommand()进行同步处理;
  2. 在函数syncCommand中,将调用函数rdbSaveBackground启动一个备份进程用于数据同步,如果已经有一个备份进程在运行了,就不会再重新启动了。
  3. 备份进程将执行函数rdbSave()完成将redis的全部数据保存为rdb文件。
  4. 在redis的时间事件函数serverCron(redis的时间处理函数是指它会定时被redis进行操作的函数)中,将对备份后的数据进行处理,在serverCron函数中将会检查备份进程是否已经执行完毕,如果备份进程已经完成备份,则调用函数backgroundSaveDoneHandler完成后续处理。
  5. 在函数backgroundSaveDoneHandler中,首先更新master的各种状态,例如,备份成功还是失败,备份的时间等等。然后调用函数updateSlavesWaitingBgsave,将备份的rdb数据发送给等待的slave。
  6. 在函数updateSlavesWaitingBgsave中,将遍历所有的等待此次备份的slave,将备份的rdb文件发送给每一个slave。另外,这里并不是立即就把数据发送过去,而是将为每个等待的slave注册写事件,并注册写事件的响应函数sendBulkToSlave,即当slave对应的socket能够发送数据时就调用函数sendBulkToSlave(),实际发送rdb文件的操作都在函数sendBulkToSlave中完成。
  7. sendBulkToSlave函数将把备份的rdb文件发送给slave。

上述函数调用过程如下图1所示:

image.pngimage.png

数据修改操作的同步

Redis的正常部署中一般都是一个master用于写操作,若干个slave用于读操作,另外定期的数据备份操作也是单独选址一个slave完成,这样可以最大程度发挥出redis的性能。在部署完成,各master\slave程序启动之后,首先进行第一阶段初始化时的全同步操作,全同步操作完成之后,后续所有写操作都是在master上进行,所有读操作都是在slave上进行,因此用户的写操作需要及时扩散到所有的slave以便保持数据最大程度上的同步。Redis的master-slave进程在正常运行期间更新操作(包括写、删除、更改操作)的同步方式如下:

  1. master接收到一条用户的操作后,将调用函数call函数来执行具体的操作函数(此过程可参考另一文档《redis命令执行流程分析》),在该函数中首先通过proc执行操作函数,然后将判断操作是否需要扩散到各slave,如果需要则调用函数propagate()来完成此操作。

  2. propagate()函数完成将一个操作记录到aof文件中或者扩散到其他slave中;在该函数中通过调用feedAppendOnlyFile()将操作记录到aof中,通过调用replicationFeedSlaves()将操作扩散到各slave中。

  3. 函数feedAppendOnlyFile()中主要保存操作到aof文件,在该函数中首先将操作转换成redis内部的协议格式,并以字符串的形式存储,然后将字符串存储的操作追加到aof文件后。

  4. 函数replicationFeedSlaves()主要将操作扩散到每一个slave中;在该函数中将遍历自己下面挂的每一个slave,以此对每个slave进行如下两步的处理:将slave的数据库切换到本操作所对应的数据库(如果slave的数据库id与当前操作的数据id不一致时才进行此操作);将命令和参数按照redis的协议格式写入到slave的回复缓存中。写入切换数据库的命令时将调用addReply,写入命令和参数时将调用addReplyMultiBulkLen和addReplyBulk,函数addReplyMultiBulkLen和addReplyBulk最终也将调用函数addReply。

  5. 在函数addReply中将调用prepareClientToWrite()设置slave的socket写入事件处理函数sendReplyToClient(通过函数aeCreateFileEvent进行设置),这样一旦slave对应的socket发送缓存中有空间写入数据,即调用sendReplyToClient进行处理。

  6. 函数sendReplyToClient()的主要功能是将slave中要发送的数据通过socket发出去。

image.pngimage.png

数据同步相关核心机制

第一次slave连接master的时候,执行的是全量复制,这个过程中有些细节的机制

    1. master和slave都会维护一个offset

      master会在自身不断累加offset,slave也会在自身不断累加offset。slave每秒都会上报自己的offset给master,同时master也会保存每个slave的offset。

      offset并不是只用在全量复制中,主要是master和slave都要知道各自的数据offset,才能知道互相之间数据不一致的情况

    2. backlog机制

      master node有一个backlog,默认大小是1M,master node给slave node复制数据时,也会将数据backlog中同步写一份,backlog主要是用来做全量复制中断时候的增量复制

    3. master run id

      在redis中执行info server命令,可以看到master run id,如果根据host+ip定位master node,是不准确的,如果master node重启或者数据出现了变化,那么slave node应该根据不同的run id区分,run id不同就做全量复制。
      如果需要不更改run id重启redis,可以使用redis-cli debug reload命令

    4. psync命令

      从节点使用psync从master node进行复制,psync runid offset,master node会根据自身的情况返回响应信息,可能是FULLRESYNC runid offset触发全量复制,可能是CONTINUE触发增量复制

    5. heatbeat机制

      主从节点互相都会发送heartbeat信息,master默认每隔10秒发送一次heartbeat,slave node每隔1秒发送一个heartbeat

posted @ 2020-04-19 08:10  一枝花算不算浪漫  阅读(732)  评论(0编辑  收藏  举报