Redis复制的原理和优化

什么是主从复制

单机有什么问题?

机器故障,容量瓶颈,QBS瓶颈,

主从复制的作用

提供了一主多从,数据副本,扩展读性能

简单总结:

1.一个master(主)可以有多个slave

2.一个slace只能有一个master

3.数据流向是单向的,master到slave


主从复制的配置

两种实现方式: slaveof命令 ,配置

slaveof命令: slaveof no one(取消复制,就取消掉主从的连接) 从表配置连接主表slaveof ip port (主从连接)

修改配置:

slaveof ip port  (连接主节点)

slave-read-only yes (只读)

两种方式的比较:

命令:

优点:无需重启

缺点:不便于管理

统一配置:

优点:统一配置管理

缺点:需要重启


全量复制和部分复制

全量复制:

redis全量复制的原理是,首先将master本身的RDB文件同步给slave,而在同步期间,master写入的命令也会记录下来(master内部有一个复制缓冲区,会记录同步时master新增的写入),当slave将RDB加载完后,会通过偏移量的对比将这期间master写入的值同步给slave。

来看一张完整的复制流程图:


1. slave内部首先会发送一个psync的命令给master 这个命令第一个参数是runId,第二个参数是偏移量,而由于是第一次复制,slave不知道master的runId,也不知道自己偏移量,这时候会传一个问号和-1,告诉master节点是第一次同步。

2. 当master接受到psync ? -1 时,就知道slave是要全量复制,就会将自己的runID和offset告知slave

3.slave会将master信息保存

4.master这时会做一个RDB的生成(bgsave)

5.将RDB发送给slave

6.将复制缓冲区记录的操作也发送给slave

7.slave清空自己的所有老数据

8.slave这时就会加载RDB文件以及复制缓冲区数据,完成同步。

全量复制的开销:

1.bgsave的开销,每次bgsave需要fork子进程,对内存和CPU的开销很大

2.RDB文件网络传输的时间(网络带宽)

3.从节点清空数据的时间

4.从节点加载RDB的时间

5.可能的AOF重写时间(如果我们的从节点开启了AOF,则加载完RDB后会对AOF进行一个重写,保证AOF是最新的)


部分复制:

为什么要部分复制? 在redis2.8版本之前,如果master和slave之间的网络发生了抖动连接断开,就会导致slave完全不知道master的动作,同步就会出问题,而为了保证数据一致,等网络恢复后进行一次全量复制。而全量复制的开销是很大的,redis2.8版本就提个了一个部分复制的功能。

部分复制的实现原理:

当master和slave断开连接时,master会将期间所做的操作记录到复制缓存区当中(可以看成是一个队列,其大小默认1M)。待slave重连后,slave会向master发送psync命令并传入offset和runId,这时候,如果master发现slave传输的偏移量的值,在缓存区队列范围中,就会将从offset开始到队列结束的数据传给slave,从而达到同步,降低了使用全量复制的开销。

注意:

正常情况下redis是如何决定是全量复制还是部分复制?

从节点将offset发送给主节点后,主节点根据offset和缓冲区大小决定能否执行部分复制:

1.如果offset偏移量之后的数据,仍然都在复制积压缓冲区里,则执行部分复制;

2.如果offset偏移量之后的数据已不在复制积压缓冲区中(数据已被挤出),则执行全量复制。

服务器运行ID(runid)

每个Redis节点(无论主从),在启动时都会自动生成一个随机ID(每次启动都不一样),由40个随机的十六进制字符组成;runid用来唯一识别一个Redis节点。 通过info server命令,可以查看节点的runid。

主从节点初次复制时,主节点将自己的runid发送给从节点,从节点将这个runid保存起来;当断线重连时,从节点会将这个runid发送给主节点;主节点根据runid判断能否进行部分复制:

如果从节点保存的runid与主节点现在的runid相同,说明主从节点之前同步过,主节点会继续尝试使用部分复制(到底能不能部分复制还要看offset和复制积压缓冲区的情况)

如果从节点保存的runid与主节点现在的runid不同,说明从节点在断线前同步的Redis节点并不是当前的主节点,只能进行全量复制。

缓冲区大小调节:

由于缓冲区长度固定且有限,因此可以备份的写命令也有限,当主从节点offset的差距过大超过缓冲区长度时,将无法执行部分复制,只能执行全量复制。反过来说,为了提高网络中断时部分复制执行的概率,可以根据需要增大复制积压缓冲区的大小(通过配置repl-backlog-size)来设置;例如如果网络中断的平均时间是60s,而主节点平均每秒产生的写命令(特定协议格式)所占的字节数为100KB,则复制积压缓冲区的平均需求为6MB,保险起见,可以设置为12MB,来保证绝大多数断线情况都可以使用部分复制。


故障处理

A是redis主服务器,B是从服务器。A机器故障暂无法修复。现将C机器上的redis服务作为主服务器替换上去。

具体的替换计划是这样的:

首先向B发送一个SAVE命令,让它创建一个最新的快照文件

接着将这个快照文件发送给机器C,并在机器C上面启动Redis

在机器B上执行slaveof 命令,让它将C机器作为主服务器




主从复制开发运维常见问题

一.读写分离:读流量分摊到从节点。


可能遇到的问题:

1.数据复制的延迟

Redis 复制数的延迟由于异步复制特性是无法避免的,延迟取决于网络带宽和命令阻塞情况,对于无法容忍大量延迟场景,可以编写外部监控程序监听主从节点的复制偏移量,当延迟较大时触发报警或者通知客户端避免读取延迟过高的从节点,实现逻辑如下图:


说明如下:

1) 监控程序(monitor) 定期检查主从节点的偏移量,主节点偏移量在info replication 的master_repl_offset 指标记录,从节点 偏移量可以查询主节点的slave0 字段的offset指标,它们的差值就是主从节点延迟的字节量。

2)当延迟字节量过高时,比如超过10M。监控程序触发报警并通知客户端从节点延迟过高。可以采用Zookeeper的监听回调机制实现客户端通知。

3) 客户端接到具体的从节点高延迟通知后,修改读命令路由到其他从节点或主节点上。当延迟恢复后,再次通知客户端,恢复从节点的读命令请求。

这种方案成本较高,需要单独修改适配Redis的客户端类库。


2.读到过期数据

当主节点存储大量设置超时的数据时,如缓存数据,Redis内部需要维护过期数据删除策略,删除策略主要有两种:惰性删除和定时删除。

惰性删除:主节点每次处理读取命令时,都会检查key是否超时,如果超时则执行del命令删除key对象那个,之后del命令也会异步 发送给 从节点


需要注意的是为了保证复制的一致性,从节点自身永远不会主动删除超时数据,如上图。

定时删除:

Redis主节点在内部定时任务会循环采样一定数量的key,当发现采样的key过期就执行del命令,之后再同步给从节点,如下图


如果此时 数据的大量超时,主节点采样速度跟不上过期速度且主节点没有读取过期key的操作,那么从节点将无法收到del命令,这时在从节点 上可以读取到已经超时的数据。Redis在3.2 版本解决了这个问题,从节点 读取数据之前会检查键的过期时间来决定是否返回数据,可以升级到3.2版本来规避这个问题。


3.从节点故障问题

对于从节点故障问题,需要在客户端维护可用从节点列表,当从节点故障时立刻切换到其他从节点或主节点上。

建议大家在做读写分离时,可以考虑使用Redis Cluster(分布式),Twemproxy(代理分片,Codis集群等分布式解决方案



二.主从配置不一致

1. max memory配置不一致:这个会导致数据的丢失。

原因:例如master配置4G,slave配置2G,这个时候主从复制可以成功,但如果在进行某一次全量复制的时候,slave拿到master的RDB加载数据时发现自身的2G内存不够用,这时就会触发slave的maxmemory策略,将数据进行淘汰。更可怕的是,在高可用的集群环境下,如果将这台slave升级成master的时候,就会发现数据已经丢失了,而且无法挽回了,这个有些时候会出现一些诡异的情况,当然,我们可以使用一些标准的工具进行一个安装,并且对其进行一个监控。


2. 数据结构优化参数不一致(例如hash-max-ziplist-entries):这个就会导致内存不一致。

原因:例如在master上对这个参数进行了优化,而在slave没有配置,就会造成主从节点内存不一致的诡异问题。


三.规避全量复制

1.第一次全量复制

当某一台slave第一次去挂到master上时,是不可避免要进行一次全量复制的,那么如何去想办法降低开销呢?

方案1:小主节点,例如把redis分成2G一个节点,这样一来会加速RDB的生成和同步,同时还可以降低fork子进程的开销(master会fork一个子进程来生成同步需要的RDB文件,而fork是要拷贝内存快的,如果主节点内存太大,fork的开销就大)。

方案2:既然第一次不可以避免,那可以选在集群低峰的时间(凌晨)进行slave的挂载。


2.节点运行ID(run_id)不匹配

例如主节点重启(RunID发生变化),对于slave来说,它会保存之前master节点的RunID,如果它发现了此时master的RunID发生变化,那它会认为这是master过来的数据可能是不安全的,就会采取一次全量复制。

方案1:对于这类问题,只有是做一些故障转移的手段,例如master发生故障宕掉,选举一台slave晋升为master(哨兵或集群)。

方案2:Redis提供了debug reload的重启方式:重启后,主节点的runid和offset都不受影响,避免了全量复制。


3.复制积压缓冲区不足

复制缓冲区空间不足,比如默认值1M,可以部分复制。但如果缓存区不够大的话,首先需要网络中断,部分复制就无法满足。其次需要增大复制缓冲区配置(relbacklogsize),对网络的缓冲增强。

master生成RDB同步到slave,slave加载RDB这段时间里,master的所有写命令都会保存到一个复制缓冲队列里(如果主从直接网络抖动,进行部分复制也是走这个逻辑),待slave加载完RDB后,拿offset的值到这个队列里判断,如果在这个队列中,则把这个队列从offset到末尾全部同步过来,这个队列的默认值为1M。而如果发现offset不在这个队列,就会产生全量复制。

方案1:增大复制缓冲区的配置 rel_backlog_size 默认1M,我们可以设置大一些,从而来加大offset的命中率。这个值,可以假设,一般网络故障时间是分钟级别,那可以根据当前的QPS来算一下每分钟可以写入多少字节,再乘以可能发生故障的分钟就可以得到我们这个理想的值。


四.规避复制风暴

什么是复制风暴?举例:master重启,其master下的所有slave检测到RunID发生变化,导致所有从节点向主节点做全量复制。尽管redis对这个问题做了优化,即只生成一份RDB文件,但需要多次传输,仍然开销很大。

1.但主节点复制风暴:主节点重启,多从节点全量复制

解决方案:更换复制拓扑,如下图:

1.将原来master与slave中间加一个或多个slave,再在slave上加若干个slave,这样可以分担所有slave对master复制的压力。(这种架构还是有问题:读写分离的时候,slave1也发生了故障,怎么去处理?)

2.如果只是实现高可用,而不做读写分离,那当master宕机,直接晋升一台slave即可。

2.单机器复制风暴:机器宕机后的大量全量复制,如下图:

方案1:应该把主节点尽量分散在多台机器上,避免在单台机器上部署过多的主节点。

当主节点所在机器故障后提供故障转移机制,避免机器恢复后进行密集的全量复制

方案2:还有我们可以采用高可用手段(slave晋升master)就不会有类似问题了。



Redis常见性能问题和解决办法

1)Master写内存快照

save命令调度rdbSave函数,会阻塞主线程的工作,当快照比较大时对性能影响是非常大的,会间断性暂停服务,所以Master最好不要写内存快照。

2)Master AOF持久化

如果不重写AOF文件,这个持久化方式对性能的影响是最小的,但是AOF文件会不断增大,AOF文件过大会影响Master重启的恢复速度。

3)Master调用BGREWRITEAOF

Master调用BGREWRITEAOF重写AOF文件,AOF在重写的时候会占大量的CPU和内存资源,导致服务load过高,出现短暂服务暂停现象。

下面是我的一个实际项目的情况,大概情况是这样的:一个Master,4个Slave,没有Sharding机制,仅是读写分离,Master负责 写入操作和AOF日志备份,AOF文件大概5G,Slave负责读操作,当Master调用BGREWRITEAOF时,Master和Slave负载会 突然陡增,Master的写入请求基本上都不响应了,持续了大概5分钟,Slave的读请求过也半无法及时响应,Master和Slave的服务器负载图 如下:

Master Server load:

Slave server load:

上面的情况本来不会也不应该发生的,是因为以前Master的这个机器是Slave,在上面有一个shell定时任务在每天的上午10点调用 BGREWRITEAOF重写AOF文件,后来由于Master机器down了,就把备份的这个Slave切成Master了,但是这个定时任务忘记删除 了,就导致了上面悲剧情况的发生,原因还是找了几天才找到的。

将no-appendfsync-on-rewrite的配置设为yes可以缓解这个问题,设置为yes表示rewrite期间对新写操作不fsync,暂时存在内存中,等rewrite完成后再写入。最好是不开启Master的AOF备份功能。

4)Redis主从复制的性能问题

第一次Slave向Master同步的实现是:Slave向Master发出同步请求,Master先dump出rdb文件,然后将rdb文件全量 传输给slave,然后Master把缓存的命令转发给Slave,初次同步完成。第二次以及以后的同步实现是:Master将变量的快照直接实时依次发 送给各个Slave。不管什么原因导致Slave和Master断开重连都会重复以上过程。Redis的主从复制是建立在内存快照的持久化基础上,只要有 Slave就一定会有内存快照发生。虽然Redis宣称主从复制无阻塞,但由于Redis使用单线程服务,如果Master快照文件比较大,那么第一次全 量传输会耗费比较长时间,且文件传输过程中Master可能无法提供服务,也就是说服务会中断,对于关键服务,这个后果也是很可怕的。


以上1.2.3.4根本问题的原因都离不开系统IO瓶颈问题,也就是硬盘读写速度不够快,主进程 fsync()/write() 操作被阻塞。


5)单点故障问题

由于目前Redis的主从复制还不够成熟,所以存在明显的单点故障问题,这个目前只能自己做方案解决,如:主动复制,Proxy实现Slave对 Master的替换等,这个也是目前比较优先的任务之一。


简单总结:

-  Master最好不要做任何持久化工作,包括内存快照和AOF日志文件,特别是不要启用内存快照做持久化。

-  如果数据比较关键,某个Slave开启AOF备份数据,策略为每秒同步一次。

-  为了主从复制的速度和连接的稳定性,Slave和Master最好在同一个局域网内。

-  尽量避免在压力较大的主库上增加从库

-  为了Master的稳定性,主从复制不要用图状结构,用单向链表结构更稳定,即主从关系 为:Master<–Slave1<–Slave2<–Slave3…….,这样的结构也方便解决单点故障问题,实现Slave对 Master的替换,也即,如果Master挂了,可以立马启用Slave1做Master,其他不变。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 213,928评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,192评论 3 387
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 159,468评论 0 349
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,186评论 1 286
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,295评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,374评论 1 292
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,403评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,186评论 0 269
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,610评论 1 306
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,906评论 2 328
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,075评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,755评论 4 337
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,393评论 3 320
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,079评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,313评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,934评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,963评论 2 351

推荐阅读更多精彩内容