单机的redis,能够承载的QPS大概是在上万到几万不等。对于缓存来说,一般都是用来支撑读高并发的。因此架构做成主从(master-slave)架构,一主多从,主负责写入数据,并将数据复制到其他的slave节点。从负责读取数据,所有的读请求全部走从节点,这样可以很轻松实现水平扩容,支撑读高并发。
image.png
redis replication -> 主从架构 -> 读写分离 -> 水平扩容支撑读高并发

redis replication的核心机制

  • redis 采用异步方式 复制数据到slave节点。在reids2.8开始,slave节点 会周期性地确认自己每次复制的数据量。
  • 一个 master节点可以配置多个slave节点。
  • slave节点可以连接其他的slave节点。
  • slave节点做复制的时候,不会阻塞master节点的正常工作。
  • slave节点做复制的时候,也不会阻塞对自己的查询操作,它会用旧的数据集来提供服务,但是复制完成的时候,需要删除旧数据集,加载新数据集,这个时候就会暂停对外服务。
  • slave节点主要用来进行横向扩容,做读写分离,扩容的slave节点可以提高读的吞吐量。

注意,如果采用了主从架构,那么建议必须开启master node 的持久化,不建议用slave节点作为master节点的数据热备,因为那样的话,如果你关掉了master的持久化,可能在master节点重启宕机的时候数据是空的,然后可能一经过复制,slave节点的数据也丢失了。
另外,master的各种备份方案也需要做。万一本地的所有文件丢失了,从备份中挑选一份rdb文件去恢复master,这样才能确保启动的时候,是有数据的。

redis主从复制的核心原理

(1)当启动一个slave节点的时候,它会发送一个 psync 命令给master节点。
(2)如果slave节点是初次连接master节点,就会触发一次 full resynchronization 全量复制。此时master会启动一个后台线程,开始生成一份 RDB 快照文件,同时还会将从客户端新收到的所有写命令缓存在内存中。 RDB 文件生成完毕后,master会将这个RDB文件发送给slave节点,slave节点会先写入本地磁盘,然后再从本地磁盘加载到内存中,接着master会将内存中缓存的写命令发送给slave,slave也会同步这些数据。
(3)slave节点如果跟master节点有网络故障,断开了连接,会自动重连,连接之后master节点仅会复制给slave节点部分缺少的数据。
image.png

主从复制的断点续传

redis2.8支持断点续传功能。如果在主从复制的过程中,网络中断导致复制过程中断,那再恢复之后,可以接着上一次复制的地方,继续复制下去,而不是从头开始复制一份。

master节点内部维护一个backlog,master和slave都会保存一个replica offset还有一个master run id,offset就是保存在backlog中的。如果master和slave网络连接中断,slave会在网络恢复之后,让master从上次的replica offset开始继续复制,如果没有找到offset的话,那么就会执行一次 resynchronization

master run id:每次master节点重启或者数据发生变化,此时对应的run id也会发生变化。那么slave节点再同步数据的过程中,就应该根据不同的run id进行区分,否则会造成数据同步错误。

无磁盘化复制

master直接在内存中创建rdb文件,然后发送给slave节点,不会再本地落地磁盘了。
只需要在配置文件中开启repl-diskless-sync yes 即可。

  1. repl-diskless-sync yes
  2. # 等待 5s 后再开始复制,因为要等更多 slave 重新连接过来
  3. repl-diskless-sync-delay 5

过期key的处理

slave节点不会过期key,只会等待master节点内的key过期,或者lru淘汰一个key,master会模拟一条del命令发送给slave节点。

复制的完整流程

(1)slave节点启动,会在本地保存master节点信息,包括host和ip,但是复制流程还没开始。
(2)slave节点内部有个定时任务,每秒检查是否有新的master节点要连接和复制。
(3)如果发现,就跟master节点建立socket连接,然后slave 节点发送ping命令给master节点。如果master节点设置了requirepass,那么slave节点必须发送masterauth的口令过去进行验证。
(4)验证通过后,master节点在第一次复制会进行全量复制,生成一份rdb文件,然后发送给slave节点。
(5)同时在发送过程中,客户端对master节点的相关写命令也会缓存在redis指定的内存缓冲区里,在发送完毕之后,同样会将这些缓存的谢明令复制给slave节点。

image.png

全量复制

  • master执行bgsave,在本地生成一份rdb快照文件。
  • master node将rdb快照文件发送给slave node,如果rdb复制时间超过60秒(rel-timeout),那么slave node就会认为复制失败。 可以适当调大这个参数rel-timeout(对于千兆网卡的机器,一般每秒传输100MB,6GB文件很可能超过60s)
  • master node在生成rdb时,会将所有新的写命令缓存在内存中,在slave node保存了rdb文件后,再将新的写命令复制给slave node。
  • 如果在复制期间,内存缓冲区持续消耗超过64MB,或者一次性超过了256MB,那么就停止复制,复制失败。

    1. client-output-buffer-limit slave 256MB 64MB 60
  • slave在接收到rdb之后,在加载rdb文件过程中,数据的读取还是基于原来旧的数据版本,等到rdb文件加载完毕之后,就会清空原来的旧数据,数据的读取就基于新的数据版本。

  • 如果slave node 开启了AOF,那么会立即执行BGREWRITEAOF,重写AOF。

增量复制

  • 如果全量复制过程中,出现了网络连接断开的情况,那么在slave重新连接master时,就会触发增量复制。
  • master直接从自己的backlog中获取部分丢失的数据,发送给slave node,默认backlog就是1MB。
  • master就是根据slave发送的psync中的offset来从backlog中获取数据的。

心跳机制

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

异步复制

master每次接收到写命令之后,会现在内部写入数据,然后异步发送给slave节点。