怎么保证Redis是高并发以及高可用的?从石杉码农课程整理而来node
redis,你要搞高并发的话,不可避免,要把底层的缓存搞得很好mysql
mysql,高并发,作到了,那么也是经过一系列复杂的分库分表,订单系统,事务要求的,QPS到几万,比较高了redis
要作一些电商的商品详情页,真正的超高并发,QPS上十万,甚至是百万,一秒钟百万的请求量算法
光是redis是不够的,可是redis是整个大型的缓存架构中,支撑高并发的架构里面,很是重要的一个环节sql
首先,你的底层的缓存中间件,缓存系统,必须可以支撑的起咱们说的那种高并发,其次,再通过良好的总体的缓存架构的设计(多级缓存架构、热点缓存),支撑真正的上十万,甚至上百万的高并发缓存
单机安全
单机的redis几乎不太可能说QPS超过10万+,除非一些特殊状况,好比你的机器性能特别好,配置特别高,物理机,维护作的特别好,并且你的总体的操做不是太复杂bash
单机在几万网络
读写分离,通常来讲,对缓存,通常都是用来支撑读高并发的,写的请求是比较少的,可能写请求也就一秒钟几千,一两千架构
大量的请求都是读,一秒钟二十万次读
读写分离
主从架构 -> 读写分离 -> 支撑10万+读QPS的架构
课程大纲
一、图解redis replication基本原理 二、redis replication的核心机制 三、master持久化对于主从架构的安全保障的意义
redis replication -> 主从架构 -> 读写分离 -> 水平扩容支撑读高并发
redis replication的最最基本的原理,铺垫
(1)redis采用异步方式复制数据到slave节点,不过redis 2.8开始,slave node会周期性地确认本身每次复制的数据量 (2)一个master node是能够配置多个slave node的 (3)slave node也能够链接其余的slave node (4)slave node作复制的时候,是不会block master node的正常工做的 (5)slave node在作复制的时候,也不会block对本身的查询操做,它会用旧的数据集来提供服务; 可是复制完成的时候,须要删除旧数据集,加载新数据集,这个时候就会暂停对外服务了 (6)slave node主要用来进行横向扩容,作读写分离,扩容的slave node能够提升读的吞吐量
slave,高可用性,有很大的关系
若是采用了主从架构,那么建议必须开启master node的持久化!
不建议用slave node做为master node的数据热备,由于那样的话,若是你关掉master的持久化,可能在master宕机重启的时候数据是空的,而后可能一通过复制,slave node数据也丢了
master -> RDB和AOF都关闭了 -> 所有在内存中
master宕机,重启,是没有本地数据能够恢复的,而后就会直接认为本身的数据是空的
master就会将空的数据集同步到slave上去,全部slave的数据所有清空
100%的数据丢失
即便采用了后续讲解的高可用机制,slave node能够自动接管master node,可是也可能sentinal尚未检测到master failure,master node就自动重启了,仍是可能致使上面的全部slave node数据清空故障
当启动一个slave node的时候,它会发送一个PSYNC命令给master node
若是这是slave node从新链接master node,那么master node仅仅会复制给slave部分缺乏的数据; 不然若是是slave node第一次链接master node,那么会触发一次full resynchronization
开始full resynchronization的时候,master会启动一个后台线程,开始生成一份RDB快照文件,同时还会将从客户端收到的全部写命令缓存在内存中。RDB文件生成完毕以后,master会将这个RDB发送给slave,slave会先写入本地磁盘,而后再从本地磁盘加载到内存中。而后master会将内存中缓存的写命令发送给slave,slave也会同步这些数据。
slave node若是跟master node有网络故障,断开了链接,会自动重连。master若是发现有多个slave node都来从新链接,仅仅会启动一个rdb save操做,用一份数据服务全部slave node。
从redis 2.8开始,就支持主从复制的断点续传,若是主从复制过程当中,网络链接断掉了,那么能够接着上次复制的地方,继续复制下去,而不是从头开始复制一份
master node会在内存中常见一个backlog,master和slave都会保存一个replica offset还有一个master id,offset就是保存在backlog中的。若是master和slave网络链接断掉了,slave会让master从上次的replica offset开始继续复制
可是若是没有找到对应的offset,那么就会执行一次resynchronization
master在内存中直接建立rdb,而后发送给slave,不会在本身本地落地磁盘了
repl-diskless-sync repl-diskless-sync-delay,等待必定时长再开始复制,由于要等更多slave从新链接过来
slave不会过时key,只会等待master过时key。若是master过时了一个key,或者经过LRU淘汰了一个key,那么会模拟一条del命令发送给slave。
(1)slave node启动,仅仅保存master node的信息,包括master node的host和ip,可是复制流程没开始
master host和ip是从哪儿来的,redis.conf里面的slaveof配置的
(2)slave node内部有个定时任务,每秒检查是否有新的master node要链接和复制,若是发现,就跟master node创建socket网络链接 (3)slave node发送ping命令给master node (4)口令认证,若是master设置了requirepass,那么slave node必须发送masterauth的口令过去进行认证 (5)master node第一次执行全量复制,将全部数据发给slave node (6)master node后续持续将写命令,异步复制给slave node
指的就是第一次slave链接msater的时候,执行的全量复制,那个过程里面你的一些细节的机制
(1)master和slave都会维护一个offset
master会在自身不断累加offset,slave也会在自身不断累加offset slave每秒都会上报本身的offset给master,同时master也会保存每一个slave的offset
这个倒不是说特定就用在全量复制的,主要是master和slave都要知道各自的数据的offset,才能知道互相之间的数据不一致的状况
(2)backlog
master node有一个backlog,默认是1MB大小 master node给slave node复制数据时,也会将数据在backlog中同步写一份 backlog主要是用来作全量复制中断后的增量复制的
(3)master run id
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触发增量复制
(1)master执行bgsave,在本地生成一份rdb快照文件 (2)master node将rdb快照文件发送给slave node,若是rdb复制时间超过60秒(repl-timeout),那么slave node就会认为复制失败,能够适当调节大这个参数 (3)对于千兆网卡的机器,通常每秒传输100MB,6G文件,极可能超过60s (4)master node在生成rdb时,会将全部新的写命令缓存在内存中,在slave node保存了rdb以后,再将新的写命令复制给slave node (5)client-output-buffer-limit slave 256MB 64MB 60,若是在复制期间,内存缓冲区持续消耗超过64MB,或者一次性超过256MB,那么中止复制,复制失败 (6)slave node接收到rdb以后,清空本身的旧数据,而后从新加载rdb到本身的内存中,同时基于旧的数据版本对外提供服务 (7)若是slave node开启了AOF,那么会当即执行BGREWRITEAOF,重写AOF
rdb生成、rdb经过网络拷贝、slave旧数据的清理、slave aof rewrite,很耗费时间
若是复制的数据量在4G~6G之间,那么极可能全量复制时间消耗到1分半到2分钟
(1)若是全量复制过程当中,master-slave网络链接断掉,那么slave从新链接master时,会触发增量复制 (2)master直接从本身的backlog中获取部分丢失的数据,发送给slave node,默认backlog就是1MB (3)msater就是根据slave发送的psync中的offset来从backlog中获取数据的
主从节点互相都会发送heartbeat信息
master默认每隔10秒发送一次heartbeat,slave node每隔1秒发送一个heartbeat
master每次接收到写命令以后,如今内部写入数据,而后异步发送给slave node
架构上,高可用性,99.99%的高可用性
讲的学术,99.99%,公式,系统可用的时间 / 系统故障的时间,365天,在365天 * 99.99%的时间内,你的系统都是能够哗哗对外提供服务的,那就是高可用性,99.99%
系统可用的时间 / 总的时间 = 高可用性,而后会对各类时间的概念,说一大堆解释
redis不可用是什么
单实例不可用?主从架构不可用?
不可用的后果是什么?
高并发高性能的缓存不可用了,超过mysql最大承载能力大并发的大流量会涌入mysql中,致使mysql宕机
redis的高可用架构,叫作故障转移 failover,也能够叫作主备切换。
在master node故障时,自动检测,而且将某个slave node自动切换为master node的过程,叫作主备切换。这个过程,实现了redis的主从架构下的高可用性。
一旦master故障,在很短的时间内,就会切换到另外一个master上去,可能就几分钟、几秒钟redis是不可用的。
这都依赖于sentinal node,即哨兵。
sentinal,中文名是哨兵
哨兵是redis集群架构中很是重要的一个组件,主要功能以下
(1)集群监控,负责监控redis master和slave进程是否正常工做 (2)消息通知,若是某个redis实例有故障,那么哨兵负责发送消息做为报警通知给管理员 (3)故障转移,若是master node挂掉了,会自动转移到slave node上 (4)配置中心,若是故障转移发生了,通知client客户端新的master地址
哨兵自己也是分布式的,做为一个哨兵集群去运行,互相协同工做
(1)故障转移时,判断一个master node是宕机了,须要大部分的哨兵都赞成才行,涉及到了分布式选举的问题 (2)即便部分哨兵节点挂掉了,哨兵集群仍是能正常工做的,由于若是一个做为高可用机制重要组成部分的故障转移系统自己是单点的,那就很坑爹了
目前采用的是sentinal 2版本,sentinal 2相对于sentinal 1来讲,重写了不少代码,主要是让故障转移的机制和算法变得更加健壮和简单
(1)哨兵至少须要3个实例,来保证本身的健壮性 (2)哨兵 + redis主从的部署架构,是不会保证数据零丢失的,只能保证redis集群的高可用性 (3)对于哨兵 + redis主从这种复杂的部署架构,尽可能在测试环境和生产环境,都进行充足的测试和演练
哨兵集群必须部署2个以上节点
若是哨兵集群仅仅部署了个2个哨兵实例,quorum=1
+----+ +----+
| M1 |---------| R1 |
| S1 | | S2 |
+----+ +----+
复制代码
Configuration: quorum = 1
master宕机,s1和s2中只要有1个哨兵认为master宕机就能够还行切换,同时s1和s2中会选举出一个哨兵来执行故障转移
同时这个时候,须要majority,也就是大多数哨兵都是运行的,2个哨兵的majority就是2(2的majority=2,3的majority=2,5的majority=3,4的majority=2),2个哨兵都运行着,就能够容许执行故障转移
可是若是整个M1和S1运行的机器宕机了,那么哨兵只有1个了,此时就没有majority来容许执行故障转移,虽然另一台机器还有一个R1,可是故障转移不会执行
+----+
| M1 |
| S1 |
+----+
|
+----+ | +----+
| R2 |----+----| R3 |
| S2 | | S3 |
+----+ +----+
复制代码
Configuration: quorum = 2,majority
若是M1所在机器宕机了,那么三个哨兵还剩下2个,S2和S3能够一致认为master宕机,而后选举出一个来执行故障转移
同时3个哨兵的majority是2,因此还剩下的2个哨兵运行着,就能够容许执行故障转移
课程大纲
一、两种数据丢失的状况 二、解决异步复制和脑裂致使的数据丢失
主备切换的过程,可能会致使数据丢失
(1)异步复制致使的数据丢失
由于master -> slave的复制是异步的,因此可能有部分数据还没复制到slave,master就宕机了,此时这些部分数据就丢失了
(2)脑裂致使的数据丢失
脑裂,也就是说,某个master所在机器忽然脱离了正常的网络,跟其余slave机器不能链接,可是实际上master还运行着
此时哨兵可能就会认为master宕机了,而后开启选举,将其余slave切换成了master
这个时候,集群里就会有两个master,也就是所谓的脑裂
此时虽然某个slave被切换成了master,可是可能client还没来得及切换到新的master,还继续写向旧master的数据可能也丢失了
所以旧master再次恢复的时候,会被做为一个slave挂到新的master上去,本身的数据会清空,从新重新的master复制数据
min-slaves-to-write 1 min-slaves-max-lag 10
要求至少有1个slave,数据复制和同步的延迟不能超过10秒
若是说一旦全部的slave,数据复制和同步的延迟都超过了10秒钟,那么这个时候,master就不会再接收任何请求了
上面两个配置能够减小异步复制和脑裂致使的数据丢失
(1)减小异步复制的数据丢失
有了min-slaves-max-lag这个配置,就能够确保说,一旦slave复制数据和ack延时太长,就认为可能master宕机后损失的数据太多了,那么就拒绝写请求,这样能够把master宕机时因为部分数据未同步到slave致使的数据丢失下降的可控范围内
(2)减小脑裂的数据丢失
若是一个master出现了脑裂,跟其余slave丢了链接,那么上面两个配置能够确保说,若是不能继续给指定数量的slave发送数据,并且slave超过10秒没有给本身ack消息,那么就直接拒绝客户端的写请求
这样脑裂后的旧master就不会接受client的新数据,也就避免了数据丢失
上面的配置就确保了,若是跟任何一个slave丢了链接,在10秒后发现没有slave给本身ack,那么就拒绝新的写请求
所以在脑裂场景下,最多就丢失10秒的数据
sdown和odown两种失败状态
sdown是主观宕机,就一个哨兵若是本身以为一个master宕机了,那么就是主观宕机
odown是客观宕机,若是quorum数量的哨兵都以为一个master宕机了,那么就是客观宕机
sdown达成的条件很简单,若是一个哨兵ping一个master,超过了is-master-down-after-milliseconds指定的毫秒数以后,就主观认为master宕机
sdown到odown转换的条件很简单,若是一个哨兵在指定时间内,收到了quorum指定数量的其余哨兵也认为那个master是sdown了,那么就认为是odown了,客观认为master宕机
哨兵互相之间的发现,是经过redis的pub/sub系统实现的,每一个哨兵都会往__sentinel__:hello这个channel里发送一个消息,这时候全部其余哨兵均可以消费到这个消息,并感知到其余的哨兵的存在
每隔两秒钟,每一个哨兵都会往本身监控的某个master+slaves对应的__sentinel__:hello channel里发送一个消息,内容是本身的host、ip和runid还有对这个master的监控配置
每一个哨兵也会去监听本身监控的每一个master+slaves对应的__sentinel__:hello channel,而后去感知到一样在监听这个master+slaves的其余哨兵的存在
每一个哨兵还会跟其余哨兵交换对master的监控配置,互相进行监控配置的同步
哨兵会负责自动纠正slave的一些配置,好比slave若是要成为潜在的master候选人,哨兵会确保slave在复制现有master的数据; 若是slave链接到了一个错误的master上,好比故障转移以后,那么哨兵会确保它们链接到正确的master上
若是一个master被认为odown了,并且majority哨兵都容许了主备切换,那么某个哨兵就会执行主备切换操做,此时首先要选举一个slave来
会考虑slave的一些信息
(1)跟master断开链接的时长 (2)slave优先级 (3)复制offset (4)run id
若是一个slave跟master断开链接已经超过了down-after-milliseconds的10倍,外加master宕机的时长,那么slave就被认为不适合选举为master
(down-after-milliseconds * 10) + milliseconds_since_master_is_in_SDOWN_state
接下来会对slave进行排序
(1)按照slave优先级进行排序,slave priority越低,优先级就越高 (2)若是slave priority相同,那么看replica offset,哪一个slave复制了越多的数据,offset越靠后,优先级就越高 (3)若是上面两个条件都相同,那么选择一个run id比较小的那个slave
每次一个哨兵要作主备切换,首先须要quorum数量的哨兵认为odown,而后选举出一个哨兵来作切换,这个哨兵还得获得majority哨兵的受权,才能正式执行切换
若是quorum < majority,好比5个哨兵,majority就是3,quorum设置为2,那么就3个哨兵受权就能够执行切换
可是若是quorum >= majority,那么必须quorum数量的哨兵都受权,好比5个哨兵,quorum是5,那么必须5个哨兵都赞成受权,才能执行切换
哨兵会对一套redis master+slave进行监控,有相应的监控的配置
执行切换的那个哨兵,会从要切换到的新master(salve->master)那里获得一个configuration epoch,这就是一个version号,每次切换的version号都必须是惟一的
若是第一个选举出的哨兵切换失败了,那么其余哨兵,会等待failover-timeout时间,而后接替继续执行切换,此时会从新获取一个新的configuration epoch,做为新的version号
哨兵完成切换以后,会在本身本地更新生成最新的master配置,而后同步给其余的哨兵,就是经过以前说的pub/sub消息机制
这里以前的version号就很重要了,由于各类消息都是经过一个channel去发布和监听的,因此一个哨兵完成一次新的切换以后,新的master配置是跟着新的version号的
其余的哨兵都是根据版本号的大小来更新本身的master配置的
redis高并发:主从架构,一主多从,通常来讲,不少项目其实就足够了,单主用来写入数据,单机几万QPS,多从用来查询数据,多个从实例能够提供每秒10万的QPS。
redis高并发的同时,还须要容纳大量的数据:一主多从,每一个实例都容纳了完整的数据,好比redis主就10G的内存量,其实你最多只能容纳10g的数据量。若是你的缓存要容纳的数据量很大,达到了几十g,甚至几百g,或者是几t,那你就须要redis集群,并且用redis集群以后,能够提供可能每秒几十万的读写并发。
redis高可用:若是你作主从架构部署,其实就是加上哨兵就能够了,就能够实现,任何一个实例宕机,自动会进行主备切换。