在Redis复制的基础上(不包括Redis Cluster或Redis Sentinel作为附加层提供的高可用功能),使用和配置主从复制非常简单,能使得 【Redis从服务器】(下文称R)能精确得复制 【Redis主服务器】(下文称M)的内容。
每当 R 和 M 之间的连接断开时, R 会自动重连到 M,并且无论这期间 M 发生了什么, R 都将尝试让自身成为 M 的精确副本。
1 依赖机制
该系统的运行依靠如下重要的机制:
1.1 更新 R
当一个 M 和一个 R 连接正常时, M 会发送一连串命令流保持对 R 的更新,以便将自身数据集的改变复制给 R,这包括客户端的写入、key 的过期或被逐出等
1.2 部分重同步
M、R 断连后,因为网络问题或主从意识到连接超时, R 重新连接上 M 并会尝试进行部分重同步:它会尝试只获取在断开连接期间内丢失的命令流。
1.3 全量重同步
当无法进行部分重同步时, R 会请求全量重同步。
这涉及到一个更复杂过程,比如M需创建所有数据的快照,将之发送给 R ,之后在数据集更改时持续发送命令流到 R。
Redis使用默认的异步复制,低延迟且高性能,适用于大多数 Redis 场景。但R会异步确认其从M周期接收到的数据量。
客户端可使用 WAIT 命令请求同步复制某些特定的数据。但WAIT命令只能确保在其他 Redis 实例中有指定数量的已确认的副本:在故障转移期间,由于不同原因的故障转移或是由于 Redis 持久性的实际配置,故障转移期间确认的写入操作可能仍然会丢失。
2 Redis 复制特点
- Redis 使用异步复制,R 和 M 之间异步地确认处理的数据量
- 一个 M 可有多个 R
- R 可接受其他 R 的连接
除了多个 R 可以连接到同一 M,R 间也可以像层级连接其它 R。Redis 4.0起,所有 sub-R 将会从 M 收到完全一样的复制流 - Redis 复制在 M 侧是非阻塞的
M 在一或多 R 进行初次同步或者是部分重同步时,可以继续处理查询请求 - 复制在 R 侧大部分也是非阻塞当 R 进行初次同步时,它可以使用旧数据集处理查询请求,假设在 redis.conf 中配置了让 Redis 这样做。否则,你可以配置如果复制流断开, Redis R 会返回一个 error 给客户端。但在初次同步后,旧数据集必须被删除,同时加载新的数据集。 R 在这个短暂的时间窗口内(如果数据集很大,会持续较长时间),会阻塞到来的连接请求。自 Redis 4.0 开始,可以配置 Redis 使删除旧数据集的操作在另一个不同的线程中进行,但是,加载新数据集的操作依然需要在主线程中进行并且会阻塞 R
- 复制可被用在可伸缩性,以便只读查询可以有多个 R 进行(例如 O(N) 复杂度的慢操作可以被下放到 R ),或者仅用于数据安全和高可用
- 可使用复制来避免 M 将全部数据集写入磁盘造成的开销:一种典型的技术是配置你的 M 的 redis.conf以避免对磁盘进行持久化,然后连接一个 R ,配置为不定期保存或是启用 AOF。但是,这个设置必须小心处理,因为重启的 M 将从一个空数据集开始:如果一个 R 试图与它同步,那么这个 R 也会被清空!
1 单机“危机”
- 容量瓶颈
- 机器故障
- QPS瓶颈
- 一主多从
主从复制作用
- 数据副本
- 扩展读性能
- 一个M可以有多个R
- 一个R只能有一个M
- 数据流向是单向的,M => R
2 实现复制的操作
2.1 命令:Rof
异步执行,很耗时间
无需重启,但是不便于配置管理。
2.2 配置
Rof ip port R-read-only yes
虽然可统一配置,但需重启。
3 全量复制(全量同步)
- M执行
bgsave
,在本地生成一份RDB
- M将RDB发给salve,若RDB复制时间>60s(repl-timeout)
则replica就会认为复制失败,可适当调大该参数(对于千兆网卡的机器,一般每秒传输100MB,6G文件,很可能超过60s)
M在生成RDB时,会将所有新的写命令缓存在内存中,在salve保存了rdb之后,再将新的写命令复制给salve
若在复制期间,内存缓冲区持续消耗超过64MB,或者一次性超过256MB,则停止复制,复制失败
R node接收到RDB之后,清空自己的旧数据,然后重新加载RDB到自己的内存中,同时基于旧的数据版本对外提供服务
如果R开启了AOF,那么会立即执行BGREWRITEAOF,重写AOF
RDB生成、RDB通过网络拷贝、R旧数据的清理、R aof rewrite,很耗费时间。
如果复制的数据量在4G~6G之间,那么很可能全量复制时间消耗到1分半到2分钟。
3.1 开销
- bgsave时间
- RDB文件网络传输时间
- 从节点清空数据时间
- 从节点加载RDB的时间
- 可能的AOF重写时间
3.2 细节
M 开启一个后台save进程,以便生成一个 RDB 文件。同时它开始缓冲所有从客户端接收到的新的写入命令。当后台save完成RDB文件时, M 将该RDB数据集文件发给 R, R会先将其写入磁盘,然后再从磁盘加载到内存。再然后 M 会发送所有缓存的写命令发给 R。这个过程以指令流的形式完成并且和 Redis 协议本身的格式相同。
当主从之间的连接因为一些原因崩溃之后, R 能够自动重连。如果 M 收到了多个 R 要求同步的请求,它会执行一个单独的后台保存,以便于为多个 R 服务。
加速复制
默认情况下,M接收SYNC命令后执行BGSAVE,将数据先保存到磁盘,若磁盘性能差,则写入磁盘会消耗大量性能。
因此在Redis 2.8.18时进行改进,可以设置无需写入磁盘直接发生RDB快照给R,加快复制速度。
复制SYNC策略:磁盘或套接字。仅仅接受差异就无法继续复制过程的新副本和重新连接副本需要进行所谓的“完全同步”。 RDB文件从主数据库传输到副本数据库。传输可以通过两种不同的方式进行:1)支持磁盘:Redis主服务器创建一个新过程,将RDB文件写入磁盘。后来,该文件由父进程逐步传输到副本。 2)无盘:Redis主服务器创建一个新进程,该进程将RDB文件直接写入副本套接字,而完全不接触磁盘。使用磁盘支持的复制,在生成RDB文件的同时,只要生成RDB文件的当前子级完成工作,就可以将更多副本排入队列并与RDB文件一起使用。如果使用无盘复制,则一旦传输开始,新的副本将排队,并且当当前副本终止时将开始新的传输。当使用无盘复制时,主服务器在开始传输之前等待一段可配置的时间(以秒为单位),以希望多个副本可以到达并且传输可以并行化。使用慢速磁盘和快速(大带宽)网络时,无盘复制效果更好。
修改配置:
repl-diskless-sync yes (默认no)