redis
2021-06-29 12:02:24 58 举报
AI智能生成
Redis是一个开源的,基于内存的数据结构存储系统,可以用作数据库、缓存和消息中间件。它支持多种数据结构,如字符串、列表、集合、散列和有序集合。Redis具有高速读写能力,适用于高并发场景,并且支持数据的持久化,可以将内存中的数据定期写入磁盘,以防止数据丢失。此外,Redis还提供了丰富的功能和扩展性,如事务、发布订阅、Lua脚本等。总之,Redis是一个强大而灵活的工具,可以帮助开发人员快速构建高性能的应用程序。
作者其他创作
大纲/内容
Redis
缓存是如何实现高性能的?
缓存如何是实现高并发的?
在中午高峰期,有100万用户同时访问系统A,每秒4000个请求去查询数据库
如果数据库每秒4000个请求,可能会宕机
如果数据库每秒4000个请求,可能会宕机
每秒4000个,3000个走缓存,1000个请求走数据库。
数据库支撑不了高并发,为什么缓存可以支撑高并发呢?
缓存是走内存的,内存天然就可以支撑别说是4000/s,4万/s也没问题
但是数据库一般建议不要超过2000/s
但是数据库一般建议不要超过2000/s
缓存雪崩
情景:
假设每天高峰期每秒请求5000次,缓存可以抗每秒4000次,剩下1000个请求落到数据库上。
缓存redis挂了,5000个请求直接请求到数据库,此时数据库直接崩溃。系统全死了
解决
事前
缓存必须高可用,主从+哨兵/redis cluster,避免全盘崩溃
最好不要把缓存只放在redis里面,可以在系统内部用ehcache在维护一个缓存
1.用户发送一个请求
2.收到请求,先查本地ehcache缓存,如果没有再查redis
3.如果ehcache和redis都没有,就查数据库
4.将数据库中的结果,写入ehcache和redis中
事中
本地ehcache缓存+hystrix限流&降级,避免mysql被打死
可以设置每秒2000个请求,一秒过来5000个请求,此时只会有2000个请求会通过限流组件,进入数据库
剩下3000个走降级,返回一些友好提示。
剩下3000个走降级,返回一些友好提示。
好处:
- 1.数据库绝对不会死,限流确保了每秒只会过去2000个请求。
- 2.数据库不死,对用户来说2/5是可以被处理的
- 3.只要有2/5请求可以被处理,就意味着你的系统没死,多点几次可能还能用,用户感觉只是有点故障。
事后
redis一定要做持久化,尽快恢复缓存集群,一旦重启,自动从磁盘上加载数据,恢复内存中的数据
缓存穿透
情景:
一秒5000个请求,结果其中4000个请求是黑客发出的恶意攻击
5000个请求只有1000个请求在redis中能查到,4000个请求都得去数据库里查,但是查不到,下次又去数据库里查
解决
每次系统A从数据库只要没查到,就写一个空值到缓存里 set -999 UNKNOWN
如何保证缓存与数据库双写一致性问题
只要用缓存,就会涉及缓存与数据库双写,只要是双写,就一定会有数据一致性的问题。
cache aside pattern 最经典的缓存+数据库读写模式
读的时候,先读缓存,缓存没有的话,按噩梦就读数据库,然后取出数据放入到缓存
更新的时候,先删除缓存,然后再更新数据库。
为什么是删除缓存,而不是更新缓存?
比如商品详情页的统计,可能只是修改了某个表的字段,但是统计可能还需要其他表传一些数据,然后再进行复杂的运算,导致更新缓存的代价很高。
或者一个表被频繁的修改,但是读数据没那么频繁。1分钟内修改了100次,那么缓存就要更新100次;但是缓存在一分钟内只被读取了一次,就有了大量的冷数据。
其实就是一个lazy计算的思想,不要每次都重新做复杂的计算,不管它会不会用到,而是让他需要被使用的时候再重新计算。
或者一个表被频繁的修改,但是读数据没那么频繁。1分钟内修改了100次,那么缓存就要更新100次;但是缓存在一分钟内只被读取了一次,就有了大量的冷数据。
其实就是一个lazy计算的思想,不要每次都重新做复杂的计算,不管它会不会用到,而是让他需要被使用的时候再重新计算。
最初级缓存不一致情况:
情况:先去数据库更新数据,然后删除缓存,但是删除缓存报错了没删掉,导致请求直接去缓存里拿到旧的数据,与新数据不匹配
解决:先删除缓存,再写入数据库,这样就算数据库更新失败报错,没有缓存也是去数据库拿旧数据,不会出现数据不一致情况
比较复杂的数据不一致情况
情况:
1.在更新一个库存的时候,同时在读取这个库存的缓存,并发的发生了。
2.开始执行操作,先删除缓存中的数据,但是还没来的及将库存在数据库中修改。
3.读请求去缓存里读,读不到,然后去数据库里读到旧数据,然后写到redis里。
4.这个时候才完成更新数据库系统的操作,导致数据库里的数据是新的,缓存里的数据是旧的。
--只有在高并发场景下,才会出现这种问题。
1.在更新一个库存的时候,同时在读取这个库存的缓存,并发的发生了。
2.开始执行操作,先删除缓存中的数据,但是还没来的及将库存在数据库中修改。
3.读请求去缓存里读,读不到,然后去数据库里读到旧数据,然后写到redis里。
4.这个时候才完成更新数据库系统的操作,导致数据库里的数据是新的,缓存里的数据是旧的。
--只有在高并发场景下,才会出现这种问题。
如图
解决
数据库与缓存的更新与读取进行异步串行化
1.先创建几个内存队列,根据商品的id,进行hash取值,然后根据队列数量取模,放到相应的队列里
2.每个队列通过异步一个线程进行消费,顺序消费队列里的请求。
这样的话,就能保证先完成数据库和缓存的修改,才能够进行读数据
2.每个队列通过异步一个线程进行消费,顺序消费队列里的请求。
这样的话,就能保证先完成数据库和缓存的修改,才能够进行读数据
如图
优化点
如果同一个数据有很多读请求过来,全都进行数据库的更新是没有意义的,
可以做过滤,如果发现队列中已经有一个更新缓存的请求了,就不用再放个更新请求操作进去了,直接等待前面的更细 操作请求完成即可
如果请求还在等待时间范围内,不断轮询发现可以取到值了,那么就直接返回;如果请求等待时间超过一定时长,那么这一次直接从数据库中读取当前的旧值。
高并发场景下注意一些问题
(1)读请求长时阻塞
如果数据频繁更新,导致大量更新的操作在同一个队列里面,然后读请求等待很多的写请求完成后再执行,会发生大量超时,最后导致大量的请求直接走数据库。
一定要根据实际业务情况,去进行一些压力测试,和模拟线上环境,去看看这种情况下读多久能响应
如果不够的话就加机器
(2)读请求并发量高
还是要做好压力测试,一个大的风险就是突然间大量的读请求会在几十毫秒的延迟hang在服务上,看服务器能不能扛得住。需要多少机器才能扛住极限情况的峰值
(3)多台机器,要确保对同一个商品的读写请求,全部路由到同一台机器上,否则不在同一台机器上队列也就没用了
通过某个请求的参数hash进行路由到同一个机器上
(4)热点商品的路由问题,导致请求的倾斜
万一某个商品的请求读写特别高,全部打到相同的机器的相同队列里去了,可能造成某台机器压力过大
如果没有要求最好不要做这个串行化,因为一旦进行串行化,会导致系统吞吐量大幅度降低
可能就得用比正常情况下多几倍的机器去支撑
可能就得用比正常情况下多几倍的机器去支撑
redis的缓存并发竞争问题是什么?
如何解决这个问题?
了解Redis事务的CAS方案么?
如何解决这个问题?
了解Redis事务的CAS方案么?
多客户端同时并发写一个key,可能本来应该先到的数据后到了,导致数据版本错了。或者多客户端同时获取一个key,修改值之后再写回去,只要顺序错了,数据就错了
--Redis自己就有天然解决这个问题的CAS类的乐观锁方案
--Redis自己就有天然解决这个问题的CAS类的乐观锁方案
解决:使用分布式锁
如图
1.使用分布式锁,确保同一时间内,只有一个系统的实例在操作某个key,别人都不允许写
zookeeper,redis
2.每次要写之前,要先判断一下当前这个value的时间戳是否比缓存里的value时间戳要大,如果更旧,那么就不能用旧数据覆盖新数据
用了缓存有啥后果?
缓存与数据库双写不一致
缓存并发竞争
redis和memcache有什么区别
redis比memcache拥有的数据结构更多
redis是单线程(6.0多线程),memcache是多线程
所以redis在存储小数据时性能更高,而在100k以上的数据中,memcache要高于redis
所以redis在存储小数据时性能更高,而在100k以上的数据中,memcache要高于redis
memcache原生不支持集群,redis支持cluster集群模式的。
redis线程模型
文件事件处理器
文件事件处理器是单线程的,所以redis才叫单线程的模型
流程
1.客户端连接到server sorket请求建立连接
2.serversorket接收到请求,产生一个AE_READABLE事件
3.redis内部有一个IO多路复用程序,监听所有的socket产生的事件,将事件压倒队列里
4.文件事件分派器会从队列中拿到对应的socket和事件,然后根据socket产生的事件选择事件处理器
比如文件分派处理器对AE_READABLE事件给到连接应答处理器处理这个事件
比如文件分派处理器对AE_READABLE事件给到连接应答处理器处理这个事件
三个事件处理器
连接应答处理器
命令请求处理器
命令回复处理器
5.连接应答处理器会跟客户端建立对应连接,建立相应的socket01连接客户端,然后把socket01的AE_READABLE事件和命令请求处理器关联。
6.客户端发送set请求,socket01接收到会产生AE_READABLE事件,然后被IO多路复用程序监听到产生的事件,将事件压倒队列中
7.之前连接应答处理器已经将socket01上的AE_READABLE事件跟命令请求处理器关联了,所以这个时候文件事件分派器会直接找命令请求处理器处理socket01上面新的AE_READABLE事件。
8.命令请求处理器会把socket01里的读出来请求的相关数据,进行执行和处理。
(从socket01中读取出来key和value,然后在自己内存中完成key和value的设置。)
命令请求处理器将socket01的AE_WRITABLE事件跟命令回复处理器关联起来。
(从socket01中读取出来key和value,然后在自己内存中完成key和value的设置。)
命令请求处理器将socket01的AE_WRITABLE事件跟命令回复处理器关联起来。
9.当客户端那边准备好读取响应数据时,就会在socket01上产生一个AE_WRITABLE事件,然后socket再被IO多路复用器放到队列。
由于AE_WRITABLE已经与命令回复处理器关联起来了,队列中的AE_WRITABLE就会找命令回复处理器。
命令回复处理器对socket01输出本次操作的一个结果:OK
由于AE_WRITABLE已经与命令回复处理器关联起来了,队列中的AE_WRITABLE就会找命令回复处理器。
命令回复处理器对socket01输出本次操作的一个结果:OK
10.客户端就会收到set请求的返回结果:OK
11.socket01的AE_WRITABLE事件跟命令回复处理器解除关联。
为什么redis单线程效率那么高
1秒钟可以处理几万个请求
1秒钟可以处理几万个请求
1.核心是非阻塞IO多路复用机制
IO多路复用程序,只负责轮训所有连接的socket,监听说有socket产生的请求,然后压到队列里面去。
非阻塞是指IO多路复用程序只把请求压到队列,不进行处理。
2.纯内存操作
文件事件分派器和三个处理器都是基于纯内存快速处理请求,速度非常快。微秒级。
3.单线程避免了多线程的频繁上下文切换问题
线程间切换,一个线程让出处理器使用权,就是”切出“;另外一个线程获取处理器使用权,就是切入。在切入切出过程中,操作系统会保存和恢复相关的进度信息。这个进度信息就是我们常说的“上下文”,上下文中一般包含了寄存器的存储内容以及程序计数器存储的指令内容
redis都有哪些数据类型?
分别在哪些场景比较合适?
分别在哪些场景比较合适?
string
hash
主要用来存放一些对象,后续操作的时候,可以仅仅修改对象里的某个值
list
有序列表
微博,某个大v粉丝,可以以list的格式放在redis里去缓存。
可以通过lrange,从某个元素开始读取多少个元素,分页查询。基于微博的下拉分页。
set
无序集合,自动去重
如果需要对一些数据进行快速去重,系统部署在多台服务器上,可以用redis set
交集,并集,差集的操作
两个大v的粉丝放在两个set,两个set做交集共同好友
sorted set
排序的set,去重但是可以排序,写进去的时候给一个分数,自动根据分数排序,可以玩很多花样,最大特点是分数可以自定义排序规则。
获取前几 zrevrange key 0 3
获取第几 zrank key value
获取第几 zrank key value
redis的过期策略是什么?
手写个LRU算法?
手写个LRU算法?
缓存是基于内存的,内存是有限的,如果存储数据超过内存大小,数据会被干掉。
设置了key过期时间,但是没到时间就失效了。或者数据过期时间到了,差不到了,但是还占用着内存。
如下因为删除是定期删除+惰性删除
1.假设你设置了一个一批key只能存活一个小时,那么接下来的一个小时后,
redis是怎么对这批key进行删除的?
redis是怎么对这批key进行删除的?
定期删除+惰性删除
定期删除:redis每隔100毫秒就随机抽取一些设置了过期时间的key,检查其是否过期,如果过期了就删除。
注意是随机检查一些key,不是所有的key都遍历一遍检查,否则cpu受不了。
注意是随机检查一些key,不是所有的key都遍历一遍检查,否则cpu受不了。
惰性删除:后面获取某个key的时候,redis会检查一下key是否过期,过期了就删除,不会给你返回任何东西。
并不是key到期了就被删除,而是查询key的时候,懒惰的删除。
并不是key到期了就被删除,而是查询key的时候,懒惰的删除。
2.因为过期key但是没有走惰性删除或者其他的key过多,导致内存块耗尽了,咋整?
走内存淘汰机制
内存淘汰:如果redis占用过多的时候,此时会进行内存淘汰,有如下一些策略
1.noevication:当内存不足以容纳新写入数据时,新写入操作会报错,这个一般没人用,太恶心了。
2.allkeys-lru:当内存满了,在键空间中,移除最近最少使用的key(最少使用的key)
3.allkeys-random:内存满了,随机找一些key删除。
4.volatile-lru:内存满了,只对设置了过期时间的key,使用最少的删除。
5.volatile-random:内存满了,随机删除设置了过期时间的key。
6.volatile-ttl:当内存满了,对有更早过期时间的key优先删除。
1.noevication:当内存不足以容纳新写入数据时,新写入操作会报错,这个一般没人用,太恶心了。
2.allkeys-lru:当内存满了,在键空间中,移除最近最少使用的key(最少使用的key)
3.allkeys-random:内存满了,随机找一些key删除。
4.volatile-lru:内存满了,只对设置了过期时间的key,使用最少的删除。
5.volatile-random:内存满了,随机删除设置了过期时间的key。
6.volatile-ttl:当内存满了,对有更早过期时间的key优先删除。
手写个LRU
如何通过读写分离承载QPS超过10万+
读写分离
写 master
读 slave
架构做成主从架构一主多从,主负责写,并将数据同步复制到其他的slave节点,从节点负责读。所有读请求全部走丛节点
redis relication主从复制基本原理
- 1. redis 采用异步方式复制数据到slave节点,不过 redis2.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可以提高吞吐量。
master持久化对于主从架构的安全保障意义-如果采用主从架构,那么建议必须开始master node的持久化!
1.不建议用slave node作为master node的热备数据,因为那样的话,如果关掉master的持久化,可能在master宕机重启的时候数据是空的,然后可能一经过复制,slave node节点数据也丢了。
2.master的各种备份方案,要不要做。万一本地文件丢失了;从备份中挑选一份rdb去恢复master;这样才能确保master启动的时候,是由数据的。
即使采用了高可用机制,slave node可以自动接管master node,但是sentinel还没有检测到master failure,master node就自动重启了,还是可能导致上面所有的slave node数据清空故障。
1.不建议用slave node作为master node的热备数据,因为那样的话,如果关掉master的持久化,可能在master宕机重启的时候数据是空的,然后可能一经过复制,slave node节点数据也丢了。
2.master的各种备份方案,要不要做。万一本地文件丢失了;从备份中挑选一份rdb去恢复master;这样才能确保master启动的时候,是由数据的。
即使采用了高可用机制,slave node可以自动接管master node,但是sentinel还没有检测到master failure,master node就自动重启了,还是可能导致上面所有的slave node数据清空故障。
主从架构的核心原理
- 1.当启动一个slave node的时候,它会发送一个PSYNC命令给master node
- 2.如果slave node是第一次启动,master会对slave进行full resynchronization全量复制。否则只会复制给slave部分缺少的数据。
- 3.全量复制原理:master启动一个后台线程,开始生成一份RDB文件,同时还会将从客户端收到的所有写命令缓存在内存中。RDB文件生成后,master将会发送这个RDB给slave,slave会先写入本地磁盘,然后再从本地磁盘加载到内存中。然后master会将内存中缓存的写命令发送给slave,slave也会同步这些操作。
4.slave node 如果跟master node 有网路故障,断开了连接,会自动重连。master如果发现有多个slave node都来重新连接,仅仅会启动一个rdb save 操作,用一份数据服务所有slave node。
主从复制断点续传
从2.8版本开始,支持主从复制断点续传,主从复制过程中,如果网络断了,那么可以接着上次复制的地方继续复制下去,而不是从头开始复制
master node会在内存中创建一个backlog,master 和slave都会保存一个replica offset还有一个master id,offset就是保存在backlog中的。如果master和slave网络连接断掉了,slave会让master从上次的replica offset开始继续复制。如果没找到对应的offset,那么就会执行一次resynchronization全量复制。
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 yes
repl-diskless-sync-delay 5 //等待一定时长再进行复制,等更多的slave重新连接过来再复制,因为生成RDB不容易
repl-diskless-sync yes
repl-diskless-sync-delay 5 //等待一定时长再进行复制,等更多的slave重新连接过来再复制,因为生成RDB不容易
过期key处理
slave不会主动过期key,只会等待master过期key。如果master过期了一个key,或者根据LRU淘汰了一个key,那么会模拟一条del命令发送给slave。
完整的复制流程
1.在本地保存master node 的host和ip
2.slave内部有定时任务,每秒都会check是否有master要连接,如果有就跟master建立socket网络连接
3.如果master配置了requirepass,那么slave发送masterauth口令过去认证。
4.master启动全量复制,将自己所有的数据,都发送给slave,实现数据的同步。
5.每次master接收到新的数据,都异步发送给slave node。
2.slave内部有定时任务,每秒都会check是否有master要连接,如果有就跟master建立socket网络连接
3.如果master配置了requirepass,那么slave发送masterauth口令过去认证。
4.master启动全量复制,将自己所有的数据,都发送给slave,实现数据的同步。
5.每次master接收到新的数据,都异步发送给slave node。
核心机制细节
1.master和slave都会维护一个offset
master和slave都会在自身不断累加offset
slave会每秒上报自己的offset给master,同时master也会保存每个slave的offset
这个倒不是说特定用在全量复制上,主要是master和slave都要知道各自数据的offset,才能互相知道数据不一致的情况。
slave会每秒上报自己的offset给master,同时master也会保存每个slave的offset
这个倒不是说特定用在全量复制上,主要是master和slave都要知道各自数据的offset,才能互相知道数据不一致的情况。
2.backlog
master有一个backlog,默认1MB大小
master给slave复制数据时,也会将数据在backlog中同步一份
backlog主要是做全量复制中断后的增量复制的。
master给slave复制数据时,也会将数据在backlog中同步一份
backlog主要是做全量复制中断后的增量复制的。
3.master run id
info server ,可以看到master run id
如果根据host+ip定位master node,是不靠谱的,如果master重启或者数据发生了变化,run id就会变化。
然后slave连接master发现run id不一致了,就会触发一次全量复制
如果需要不更改run id 重启redis,可以使用redis-cli debug reload命令
如果根据host+ip定位master node,是不靠谱的,如果master重启或者数据发生了变化,run id就会变化。
然后slave连接master发现run id不一致了,就会触发一次全量复制
如果需要不更改run id 重启redis,可以使用redis-cli debug reload命令
4.psync
slave节点使用psync从master节点进行复制,psync runid offset
如果runid不一致,进行全量复制 resynchronization
如果runid一致,返回CONTINUE进行增量复制
全量复制
- 1.master执行 bgsave 生成一份RDB快照文件。
- 2.master node将rdb文件发送给slave node,如果rdb复制时间超过60s(repl-timeout),那么slave就会认为复制失败,可适当调大这个参数
- 3.对于千兆网卡的机器,一般每秒传输100MB,6G文件,很可能超过60s。
- 4.master在生成rdb时,会将所有新的写命令存在缓存中,在slave node保存了rdb之后,再将新的写命令复制给slave。
- 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.master就是根据slave 发送的psync中的offset来从backlog中获取数据的。
2.master直接通过自己的backlog中获取部分丢失的数据,发送给slave node,默认backlog就是1MB
3.master就是根据slave 发送的psync中的offset来从backlog中获取数据的。
heartbeat
主从节点都会发送heartbeat
master默认每隔10s发送一次heartbeat,slave node 每隔1秒发送一次heartbeat
异步复制
master每次接收到写命令之后,先在内部写入数据,然后异步发送给slave node
redis架构如何才能做到99.99%高可用
高可用性:如果你的系统可以保证在全年,99.99%的时间内,都是处于可用的状态的,那么就可以称之为高可用性。
redis宕机出现的问题
如果一个slave挂掉了,是不会影响可用性的,还有其他slave提供相同数据下的相同的对外查询服务
但是如果master挂掉了,写缓存的时候全部失效了,slave也没有主节点给他们复制数据了,系统相当于不可用了
缓存不可用,高并发高性能的缓存不可用了,大量的流量,超过mysql承载范围内的大并发会涌入mysql,mysql宕机
缓存不可用,高并发高性能的缓存不可用了,大量的流量,超过mysql承载范围内的大并发会涌入mysql,mysql宕机
- redis怎么做到高可用?->redis高可用架构,叫做故障转移,failover,也可以叫做主备切换
- 在master故障时,自动检测,并将某个slave自动切换为master node的过程叫主备切换。这个过程,实现了redis的主从架构下的高可用性。
- 一旦master故障,在很短时间内,就会切换到另一个master上去。
哨兵 Sentinel
主要功能
1.集群监控,负责监控redis master和slave进程是否正常工作。
2.消息通知,如果某个redis实例有故障,那么哨兵负责发送消息作为报警通知给管理员。
3.故障转移,如果master节点挂了,会自动转移到slave节点上
4.配置中心,如果故障转移发生了,通知client客户端新的master地址。
哨兵也是分布式的,原因:
1.故障转移时,判断一个master节点是否宕机,需要大部分哨兵都同意才行,涉及到分布式选举的问题。
2.即使部分哨兵节点挂掉了,哨兵集群还是能正常工作的,因为如果一个作为高可用机制重要组成部分的故障转移系统本身是单点的,那就很坑爹了。
核心知识
1.哨兵至少需要三个实例,来保证自己的健壮性。
如果哨兵只部署了两个,且都在redis节点上
如果哨兵集群仅仅部署了两个哨兵实例,quorum=1-->几个哨兵同意认为master宕机可以进行切换。
同时S1,S2会选举出一个哨兵来执行故障转移的操作。
同时,需要majority(几个哨兵同意进行故障转移),也就是大多数哨兵都是运行的。2个哨兵的majority就是2(原则:3的majority就是2,5的majority就是3,4的majority就是2),两个哨兵都运行着,就可以允许故障转移
同时S1,S2会选举出一个哨兵来执行故障转移的操作。
同时,需要majority(几个哨兵同意进行故障转移),也就是大多数哨兵都是运行的。2个哨兵的majority就是2(原则:3的majority就是2,5的majority就是3,4的majority就是2),两个哨兵都运行着,就可以允许故障转移
一台机器挂掉,只剩一个哨兵,所以两个哨兵节点的故障转移没法执行了
经典的3节点哨兵集群
设置:quorum=2,majority
如果MASTER1所在机器宕机了,那么三哨兵还剩两个,s2和s3可以一致认为master宕机,然后选举出来一个进行故障转移
同时3个哨兵majority是2,所以还剩下的2个哨兵运行着,就可以允许执行故障转移。
同时3个哨兵majority是2,所以还剩下的2个哨兵运行着,就可以允许执行故障转移。
2.哨兵+redis主从的部署架构,是不会保证数据零丢失的,只能保证redis集群的高可用性。
3.对于哨兵+redis主从这种复杂的部署架构,尽量在测试环境和生产环境,都进行充足的测试和演练
底层原理
1.sdown和odown转换机制
sdown主观宕机:就一个哨兵如果自己觉得master宕机了。
odown客观宕机:如果quorum数量的哨兵都觉得一个master宕机了。
sdown达成条件很简单,如果一个哨兵ping一个master,超过is-master-down-after-milliseconds指定的毫秒数后,就主观认为master宕机。
sdown转换成odown条件很简单,如果一个哨兵在指定时间内,收到了quorum个指定数量的其他哨兵也认为master宕机了,那么就认为是odown,客观认为master宕机。
odown客观宕机:如果quorum数量的哨兵都觉得一个master宕机了。
sdown达成条件很简单,如果一个哨兵ping一个master,超过is-master-down-after-milliseconds指定的毫秒数后,就主观认为master宕机。
sdown转换成odown条件很简单,如果一个哨兵在指定时间内,收到了quorum个指定数量的其他哨兵也认为master宕机了,那么就认为是odown,客观认为master宕机。
2.哨兵和slave集群的自动发现机制
哨兵之间的互相发现,是通过redis的pub/sub系统实现的,每个哨兵都会往_sentinel_:hello 这个channel里发送一个消息,这时候所有其他哨兵都可以消费到这个消息,并感知到其他哨兵的存在。
每隔2秒钟,每个哨兵都会往自己监控的master+slaves对应的_sentinel_:hello channel发送消息,内容为自己的host,ip,runid还有master的监控配置
每隔哨兵也会监听自己监控的每个master+slaves对应的_sentinel_:hello channel ,然后去感知到同样在监听这个master+slaves的哨兵的存在,还会跟其他哨兵交换对master的监控配置,互相进行监控配置的同步。
每隔2秒钟,每个哨兵都会往自己监控的master+slaves对应的_sentinel_:hello channel发送消息,内容为自己的host,ip,runid还有master的监控配置
每隔哨兵也会监听自己监控的每个master+slaves对应的_sentinel_:hello channel ,然后去感知到同样在监听这个master+slaves的哨兵的存在,还会跟其他哨兵交换对master的监控配置,互相进行监控配置的同步。
3.slave配置的自动纠正
如果哨兵选举了一个新的master,哨兵会去让剩下的slave修改他们的配置,连接到新的master上面去
4.slave-master选举算法
考虑四个信息:
1.跟master断开连接的时长。
2.slave优先级
3.复制offset大小
4.run id
1.跟master断开连接的时长。
2.slave优先级
3.复制offset大小
4.run id
1.先排除,如果一个slave跟master断开连接的时长减掉master宕机的时长已经超过了down-after-milliseconds的10倍,那么slave就认为不适合选举为master
2.接下来对slave进行排序
(1)按照slave优先级进行排序,配置文件里设置的slave-priority越小,优先级越高
(2)slave里复制主节点的offset越靠后,说明复制的数据越多,优先级就越高
(3)如果上面两个都一样,那么就选一个run id比较小的slave
(1)按照slave优先级进行排序,配置文件里设置的slave-priority越小,优先级越高
(2)slave里复制主节点的offset越靠后,说明复制的数据越多,优先级就越高
(3)如果上面两个都一样,那么就选一个run id比较小的slave
5.quorum和majority
每次一个哨兵要做主备切换,首先需要quorum数量的哨兵认为odown,然后选举出来一个哨兵做切换。
然后这个哨兵还得的到mojority个哨兵的授权,才能正式执行切换。
如果quorum<mojority,比如5个哨兵,mojority就是3,quorum设置为2,那么3个哨兵授权就能切换。
但是如果quorum>mojority,那么就以quorum个数量的哨兵授权才能切换。
然后这个哨兵还得的到mojority个哨兵的授权,才能正式执行切换。
如果quorum<mojority,比如5个哨兵,mojority就是3,quorum设置为2,那么3个哨兵授权就能切换。
但是如果quorum>mojority,那么就以quorum个数量的哨兵授权才能切换。
6.configuration epoch
哨兵会对一套master+slaves进行监控,有相应监控的配置
执行切换的那个哨兵,会从要切换到的新master(slave->master)那里的到一个configuration epoch,这就是一个version号,每次切换的version号都必须是唯一的。
如果第一个选举出的哨兵切换失败了,那么其他哨兵,会等待failover-timeout的时间,然后接替继续执行切换,此时会重新获取一个新的configuration epoch,作为新的version号
执行切换的那个哨兵,会从要切换到的新master(slave->master)那里的到一个configuration epoch,这就是一个version号,每次切换的version号都必须是唯一的。
如果第一个选举出的哨兵切换失败了,那么其他哨兵,会等待failover-timeout的时间,然后接替继续执行切换,此时会重新获取一个新的configuration epoch,作为新的version号
7.configuration传播
哨兵完成主从切换之后,会在自己本地更新生成最新的master配置,然后同步给其他的哨兵,通过之前说的pub/sub消息机制channel。
这里上面的version 就很重要了,因为各种消息都是通过一个channel去发布和监听的,所以一个哨兵完成了一次新的主从切换之后,新的master配置是跟着新的version号的
其他的哨兵如果发现版本号大于自己目前存储的版本号,就会去更新自己的master配置
这里上面的version 就很重要了,因为各种消息都是通过一个channel去发布和监听的,所以一个哨兵完成了一次新的主从切换之后,新的master配置是跟着新的version号的
其他的哨兵如果发现版本号大于自己目前存储的版本号,就会去更新自己的master配置
异步复制,脑裂数据丢失
异步复制数据丢失
数据写入master,master还没异步发送给slave就挂掉了,sentinel选举新的master后导致这些还没来得及异步发送的数据就丢失了
解决:两个参数
min-slaves-to-write 1
min-slaves-max-lag 10
至少有一个slave,数据复制和同步的延迟都超过了10秒钟,那么这个时候master就不会再接收任何请求
client去做一些其他处理,比如
1.在client做降级,写到本地磁盘里面,在client对外接收请求,再做降级,做限流,减慢请求涌入速度。
2.client可能会采取将这个数据临时灌入一个kafka消息队列,每隔10分钟去队列里面取一次,尝试重新发挥master。
min-slaves-to-write 1
min-slaves-max-lag 10
至少有一个slave,数据复制和同步的延迟都超过了10秒钟,那么这个时候master就不会再接收任何请求
client去做一些其他处理,比如
1.在client做降级,写到本地磁盘里面,在client对外接收请求,再做降级,做限流,减慢请求涌入速度。
2.client可能会采取将这个数据临时灌入一个kafka消息队列,每隔10分钟去队列里面取一次,尝试重新发挥master。
脑裂导致数据丢失
master节点与其他节点丢失连接,其他节点选举新的master。
但是原来的master可能和客户端正常连接,客户端在脑裂时还在往原来的master里写数据。
手动解决脑裂问题,把原来的master关闭设置为slave节点,导致脑裂时客户端往原来master里发的一部分数据丢失。
但是原来的master可能和客户端正常连接,客户端在脑裂时还在往原来的master里写数据。
手动解决脑裂问题,把原来的master关闭设置为slave节点,导致脑裂时客户端往原来master里发的一部分数据丢失。
解决:通上两个参数
控制1个slave已经10s没有跟master做数据同步,就不接受client消息。
master连接不到其他slave,控制不让client再往它里写数据。
控制1个slave已经10s没有跟master做数据同步,就不接受client消息。
master连接不到其他slave,控制不让client再往它里写数据。
持久化
意义:故障恢复,比如部署了redis作为缓存,保存了一些重要的数据。
持久化主要做灾难恢复数据恢复,也可以归类到一个高可用的环节里面去
持久化主要做灾难恢复数据恢复,也可以归类到一个高可用的环节里面去
持久化+备份
通过持久化将数据写到磁盘上,然后把磁盘上的数据备份到云存储上去。保证数据不会丢失
RDB+AOF
RDB
每隔几分钟,几小时,几天,生成redis内存中的数据的一份完整的快照
优点
每个数据文件都代表了某一时刻中redis的数据,这种多数据文件的方式,非常适合做冷备,可以通过shell脚本将备份文件发送到一些远程的安全存储上去,以预定好的备份策略来定期备份redis中的数据。
RDB对redis提供对外读写服务,影响非常小,可以让redis保持高性能,因为redis只需要fork一个子进程,让子进程执行磁盘IO操作来进行RDB持久化即可。
RDB:redis写数据一般只需要写入内存,只有特定时间才会写入磁盘
AOF:每次写数据都要写入AOF,虽然有os cache,但是还是有影响
AOF:每次写数据都要写入AOF,虽然有os cache,但是还是有影响
相对于AOF持久化机制来说,基于RDB数据文件来重启和恢复redis进程,更加快速。
AOF存放的是指令日志,恢复需要读取和执行存放的指令
缺点
如果想要在redis故障时,尽可能少的丢失数据,那么RDB没有AOF好。一般来说RDB快照文件,都是每隔5分钟,或者更长时间生成一次,这个时候一旦redis宕机,可能丢失这段时间的数据。--最大的缺点,不适合做第一优先恢复方案。
RDB每次在fork子进程来执行RDB快照数据文件生成的时候,如果数据文件特别大,可能会导致对客户端提供的服务暂停数毫秒甚至数秒。
--一定不要让RDB间隔时间太长,否则每次生成RDB文件太大,对redis本身性能可能会有影响
--一定不要让RDB间隔时间太长,否则每次生成RDB文件太大,对redis本身性能可能会有影响
AOF
每来一条数据都会写入到AOF,但是实际上是写到操作系统os cache中,所以redis每隔1s会调用一次操作系统fsync操作,强制将os cache中的数据刷入到磁盘文件中。
AOF文件只有一个,会越来越大
redis中的数据是有一定限量的,但是AOF是存放每条写命令的,所以会不断的膨胀
redis中的数据是有一定限量的,但是AOF是存放每条写命令的,所以会不断的膨胀
rewrite:当AOF大到一定程度会进行rewrite操作。
基于当时redis内存中的数据,来重新构造一个更小的AOF,然后删除原来膨胀很大的AOF
基于当时redis内存中的数据,来重新构造一个更小的AOF,然后删除原来膨胀很大的AOF
优点
(1)可以更好保护数据不丢失,每隔1s就执行一次fsync操作,保证os cache数据写入磁盘中,redis挂了,最多丢失1s数据
(2)AOF日志文件以append-only模式写入,所以没有任何磁盘寻址开销,写入性能高,而且文件不容易破损,即使文件尾部破损,也很容易恢复。
(3)rewrite操作对性能影响很小
(4)AOF日志文件的命令通过非常可读的方式进行记录,这个特性非常适合做灾难性的误删除的紧急恢复。
比如某人不小心用了flush all清空了所有数据,只要这个时候后台rewrite还没有发生,那么立刻拷贝AOF文件,把最后一条flush all命令删了,再放回去,重启Redis自动恢复所有数据。
比如某人不小心用了flush all清空了所有数据,只要这个时候后台rewrite还没有发生,那么立刻拷贝AOF文件,把最后一条flush all命令删了,再放回去,重启Redis自动恢复所有数据。
缺点
(1)对于同一份文件来说,AOF日志文件通常比RDB数据快照文件更大。
(2)AOF开启后,支持的写QPS会比RDB支持的写QPS更低,因为AOF一般会配置成每秒fsync一次日志文件。
当然,每秒一次fsync,性能还是很高的。如果你要保证一条数据都不丢失,也是可以的,设置成每写一条数据,进行一次fsync,会导致redis的QPS性能大降。
当然,每秒一次fsync,性能还是很高的。如果你要保证一条数据都不丢失,也是可以的,设置成每写一条数据,进行一次fsync,会导致redis的QPS性能大降。
(3)唯一比较大缺点:做数据恢复的时候,会比较慢,还有做冷备,定期的备份,不太方便,可能要自己手写复杂的脚本去做,做冷备不台合适。
到底该如何选择
(1)不要仅仅使用RDB,因为那样会导致你丢失很多数据
(2)也不要仅仅使用AOF,两个问题。
1.通过AOF做冷备,没有RDB做冷备恢复的速度快。
2.RDB每次简单粗暴生成快照,更加健壮,可以避免AOF这种复杂的备份和恢复机制的bug
1.通过AOF做冷备,没有RDB做冷备恢复的速度快。
2.RDB每次简单粗暴生成快照,更加健壮,可以避免AOF这种复杂的备份和恢复机制的bug
(3)综合AOF和RDB,用AOF来保证数据不丢失,作为数据恢复的第一选择
用RDB来做不同程度的冷备,在AOF文件都丢失或损坏不可用的时候,还可以使用RDB进行快速的数据恢复。
用RDB来做不同程度的冷备,在AOF文件都丢失或损坏不可用的时候,还可以使用RDB进行快速的数据恢复。
总结
redis高并发
主从架构,一主多从,一般来说很多项目就足够了,单主用来写入数据,单机几万qps,多从用来查询数据,多个实例可以提供每秒10万qps。
redis高并发的同时,还需要容纳大量的数据:一主多从,每个实例都容纳了完整的数据,比如redis主就10G内存量,其实你就只能容纳10G的数据量。但如果你的缓存要容纳的数据量很大,达到几十G甚至几百G几T,那你就需要redis集群,而且用redis集群之后,可以提供可能每秒几十万的读写并发。
redis高可用
如果你用主从架构部署,其实就是加上哨兵就可以了,就可以实现任何一个实例宕机,自动进行主备切换。
redis线上生产环境时如何部署的?
redis cluster,部署了10台机器,5台机器部署了redis的主实例,另外5台机器部署了redis从实例,每个主实例挂了一个从实例,5个节点提供读写服务,每个节点的读写高峰可能可以达到每秒5万,5台机器最多时25万读写请求/s。
机器配置:32G内存+8核CPU。但是分配给redis进程的是10G内存,一般线上生产环境,redis的内存尽量不要超过10G,超过10g可能会有问题。
5台机器对外提供读写,一共50G内存。
5台机器对外提供读写,一共50G内存。
因为每个主实例都挂了一个从实例,所以时高可用的,任何一个主实例宕机,自动故障迁移,redis从实例会自动变成主实例继续提供读写服务。
往内存里写了什么数据?每条数据的大小是多少?
保险数据,每条数据10Kb,100条是1mb,10万条数据是1g。常驻内存的是200万条保单数据,占用内存是20g,仅仅不到总内存的50%
目前高峰期每秒就是3500左右的请求量
redis cluster集群模式
redis cluster集群模式的工作原理能说一下么?
在集群模式下,redis的key是如何寻址的?
分布式寻址都有哪些算法?
了解一致性hash算法么?
在集群模式下,redis的key是如何寻址的?
分布式寻址都有哪些算法?
了解一致性hash算法么?
可以在多台机器上,部署多个redis实例,每个实例存储一部分的数据,同时每个redis实例可以挂redis从实例,自动确保说,如果redis主实例挂了,会自动切换到redis从实例顶上来。
集群架构
支撑N个master node,每个master node都可以挂载多个slave节点。
读写分离的架构,对于每个master来说,写久写到master,然后读就从master对应的slave去读。
高可用,因为每个master都有slave节点,如果master挂掉,redis cluster这套机制,就会自动将某个slave切换成master
redis cluster(多master+读写分离+高可用)
我们只需要基于redis cluster去搭建redis集群即可,不需要手工去搭建replication复制+主从架构+读写分离+哨兵集群+高可用
我们只需要基于redis cluster去搭建redis集群即可,不需要手工去搭建replication复制+主从架构+读写分离+哨兵集群+高可用
redis cluster介绍
(1)自动将数据进行分片,每个master上放一部分数据。
(2)提供内置的高可用支持,部分master不可用时,还是可以继续工作的。
(2)提供内置的高可用支持,部分master不可用时,还是可以继续工作的。
在redis cluster架构下,那个redis要开放两个端口,比如一个6379,另外一个就要加1000的端口号,比如16379
16379端口号是用来进行节点间通信的,也就是cluster bus的东西,集群总线。cluster bus的通信,用来进行故障检测,配置更新,故障转移授权
cluster bus用了另外一种二进制协议,主要用于节点间进行高效的数据交换,占用更少的网络带宽和处理时间
16379端口号是用来进行节点间通信的,也就是cluster bus的东西,集群总线。cluster bus的通信,用来进行故障检测,配置更新,故障转移授权
cluster bus用了另外一种二进制协议,主要用于节点间进行高效的数据交换,占用更少的网络带宽和处理时间
数据存储的核心算法
hash算法->一致性hash算法->redis cluster的hash slot算法
用不同的算法,就决定了在多个master节点的时候,数据如何分布到这些节点上去,解决这个问题
用不同的算法,就决定了在多个master节点的时候,数据如何分布到这些节点上去,解决这个问题
hash算法
来了一个key之后,计算hash值,然后对节点数量取模,取模结果一定小于节点数量,然后放到取模对应的节点上
但是如果某台master宕机了,就得把其他两台master的数据全拿出来重新取模计算,所有数据放到两台机器上。
高并发场景下时不可接受的。
并发,一台机器挂掉了,原来是对3取模存的数据,现在对2取模取数据,会导致所有请求全部无法拿到有效的缓存。
最大问题,一个master宕机那么大量的数据就需要重新计算写入缓存。
并发,一台机器挂掉了,原来是对3取模存的数据,现在对2取模取数据,会导致所有请求全部无法拿到有效的缓存。
最大问题,一个master宕机那么大量的数据就需要重新计算写入缓存。
一致性hash算法
master分布在一个圆环上
然后有一个key过来以后,同样也是计算hash值,然后会用hash值在圆环对应的各个点上
key落在圆环上以后,就会顺时针旋转去寻找距离自己最近的一个节点。
然后有一个key过来以后,同样也是计算hash值,然后会用hash值在圆环对应的各个点上
key落在圆环上以后,就会顺时针旋转去寻找距离自己最近的一个节点。
一致性hash算法保证,任何一个master宕机,只有之前在那个master上的数据会受到影响,因为朝着顺时针走,全部在之前的master上找不到了,master宕机了。会顺时针找到下一个master,也找不到。
1/n的流量,会瞬间涌入数据库中,重新查询一次。
1/n的流量,会瞬间涌入数据库中,重新查询一次。
缓存热点问题
可能集中在某个hash区间内的值特别多,那么会导致大量的数据都涌入同一个master内,造成master的热点问题,性能出现瓶颈
虚拟节点
给每个master节点都做了均匀分布的虚拟节点
然后这样的话,在每个区间内,大量的数据,都会均匀的分布到不同的节点内,而不是按照顺时针的顺序去走,全部涌入同一个master内。
然后这样的话,在每个区间内,大量的数据,都会均匀的分布到不同的节点内,而不是按照顺时针的顺序去走,全部涌入同一个master内。
redis cluster的hash slot算法
redis cluster有固定的16384个hash slot,对每个key计算CRC16值,然后对16384取模,可以获取对应的hash slot
redis cluster中每个master都会持有一部分的hash slot,比如有3个master,那么可能每个master持有5000多个hash slot
hash slot让node的增加和移除很简单,增加一个master,就将其他master的hash slot移动部分过去,减少一个master,就将它的hash slot移动到其他master上去,移动hash slot的成本是非常低的。
客户端的api,可以对指定的数据,让他们走同一个hash slot,通过hash tag来实现。
redis cluster中每个master都会持有一部分的hash slot,比如有3个master,那么可能每个master持有5000多个hash slot
hash slot让node的增加和移除很简单,增加一个master,就将其他master的hash slot移动部分过去,减少一个master,就将它的hash slot移动到其他master上去,移动hash slot的成本是非常低的。
客户端的api,可以对指定的数据,让他们走同一个hash slot,通过hash tag来实现。
任何一台机器宕机,不影响其他节点,因为key找的是hash slot,不是机器。
redis发现某台机器宕机,会把这台机器的slot快速的分配到其他的master上
核心原理
节点间内部通信机制
集中式元数据维护和存储
集群有很多元数据,包括hash slot->node之间的映射表关系,还有master->slave之间的关系,故障的信息
典型方案:基于zookeeper集中式元数据的维护和存储
典型代表:storm,分布式的大数据实时计算引擎,集中式的元数据存储架构,底层基于zookeeper(分布式协调中间件)的集群所有元数据维护。
redis采用的是gossip协议
小道留言
小道留言
每个 master自己都会维护一份完整的元数据
一个master有变更,就会把元数据发送给其他的master,其他master收到了就会更新一下自己的元数据。
一个master有变更,就会把元数据发送给其他的master,其他master收到了就会更新一下自己的元数据。
对比一下
集中式
好处在于元数据的更新和读取,时效性比较好,一旦元数据出现了变更,立即就更新到集中式的存储中,其他节点读取的时候要立即就可以感知到。
不好在于,所有元数据的更新压力全部集中在一个地方,可能会导致元数据的存储有压力。
goosip
好处:元数据更新比较分散,不是集中在一个地方,更新请求会陆陆续续,打到所有节点上去更新,有一定延时,降低了压力;
缺点:元数据更新有延时,可能会导致集群的一些操作会有一些滞后。
10000端口号
每个节点都有一个专门用于节点间通信的端口,就是自己的端口号+10000
每个节点间个一段时间都会往另外几个节点发送ping消息,同时其他节点收到ping之后返回pong
交换的信息
故障信息,节点的增加删除,hash slot信息等等。
gossip协议
goosip协议包含多种消息:ping,pong,meet,fail等等
meet:某个节点发送meet给新加入的节点,让新节点加入到集群中,然后新节点就会开始于其他节点进行通信
其实内部就是发送了一个gossip meet信息,通知那个节点去加入我们的集群
ping:每个节点都会频繁的给其他节点发送ping,其中包含自己的状态还有自己维护的集群元数据,互相通过ping交换元数据。
pong:返回ping和meet,包含自己的状态和其他信息,也可用于信息广播和更新
fail:某个节点判断另一个节点fail之后,就发送fail给其他节点,通知其他节点,指定的节点宕机了。
ping消息深入
ping很频繁,而且要携带一些元数据,所以可能会加重网络负担
- 每个节每秒会执行10次ping,每次会选择5个最久没有通信的其他节点
- 当然如果发现某个节点通信延迟达到了cluster_node_timeout/2,那么立即发送ping,避免数据交换延时过长,落后的时间太长了
- 比如两个节点之间都10分钟没有交换数据了,那么整个集群处于严重的元数据不一致的情况,就会有问题。
- 所以cluster_node_timeout可以调节,如果调节比较大,那么会降低发送消息的频率。
- 每次ping,一个是带上自己节点的信息,还有就是带上1/10其他节点的信息,发送出去,进行数据交换。
- 至少包含3个其他节点的信息,最多包含总节点-2个其他节点的信息。
面向集群的jedis内部实现原理
开发,jedis,redis和java 客户端, redis cluster,jedis cluster api
基于重定向的客户端
redis-cli -c 自动重定向
请求重定向
- 客户端可能会挑选任意一个redis实例去发送命令,每个redis实例接收到命令,都会根据key计算对应的hash slot
- 如果在本地就在本地处理,否则返回moved给客户端,客户端进行重定向
- cluster keyslot mykey,可以查看一个key对应的hash slot是什么
- 用redis-cli的时候,可以加-c参数,支持自动的请求重定向,redis-cli接收到moved之后,会自动重定向到对应的节点执行命令。
计算hash slot
就是根据key计算CRC16的值,然后跟苦16384取模,拿到对应的hash slot
用hash tag可以手动指定key对应的slot,同一个hash tag下的key,都会在一个hash slot中,比如set mykey1:{100}和setmykey2:{100}
用hash tag可以手动指定key对应的slot,同一个hash tag下的key,都会在一个hash slot中,比如set mykey1:{100}和setmykey2:{100}
hash slot 查找
节点间通过gossip协议进行数据交换,就知道每一个hash slot在哪个节点上。
smart jedis
基于重定向的客户端,很消耗IO,因为大部分情况下,可能都会出现一次重定向,才能找到正确的节点
所以大部分客户端,比如jedis,都是smart的
本地维护一份hash slot->node的映射表缓存,大部分情况下,直接走本地缓存就可以找到hashslot ->node ,不需要通过节点进行moved重定向
所以大部分客户端,比如jedis,都是smart的
本地维护一份hash slot->node的映射表缓存,大部分情况下,直接走本地缓存就可以找到hashslot ->node ,不需要通过节点进行moved重定向
JedisCluster工作原理
- 在JedisCluster初始化的时候,就会随机选择一个node,初始化hashslot->node映射表,同时为每个节点创建一个jedisPool连接池
- 每次基于JedisCluster执行操作,首先会在本地计算key的hashslot,然后在本地缓存的映射表找到对应的节点。
- 如果node正好还持有hash slot就ok;如果说进行了reshard这样的操作,hashslot不在那个节点上了,就会返回moved
- 然后JedisCluster发现对应节点返回moved,那么利用该节点的元数据,更新本地的hash slot ->node的映射表缓存
- 重复上面步骤,知道找到对应节点,如果重试超过5次,那么就报错,JedisClusterMaxRedirectionException。
hashslot迁移和ask重定向
如果hash slot正在迁移,那么会返回ask给jedis
jedis接到ask重定向之后,会重定向到目标节点去执行,但是因为ask发生在hash slot迁移过程中,所以不会更新本地缓存的映射表
高可用性与主备切换
redis cluster的高可用原理,几乎跟哨兵是类似的
1.判断节点宕机
如果一个节点认为另外一个节点宕机了,那么就是pfail,主观宕机
如果多个节点都认为另外一个节点宕机了,就是fail,客观宕机,跟哨兵几乎一样 sdown,odown
如果多个节点都认为另外一个节点宕机了,就是fail,客观宕机,跟哨兵几乎一样 sdown,odown
根据cluster-node-timeout内某个节点一直没有返回pong,那么就会被认为pfail
如果一个节点认为某个节点pfail了,那么就会在gossip消息中,ping给其他节点,如果超过半数节点都认为pfail了,那么就会变成fail
2.从节点过滤
对宕机的master node,从其所有slave node中,选择一个切换成master node
检查每个slave node与master node 的断开时间,如果超过了cluster-node-timeout*cluster-slave-validity-factory,那么久没有资格切换成master
3.从节点选举
每个从节点,都根据自己对master复制数据的offset,来设置一个选举时间,offset越大(复制数据越多)的从节点,选举时间越靠前,优先进行选举
所有的master node开始slave选举投票,给要进行选举的slave进行投票,如果大部分master node(n/2+1)都投票给了某个从节点,那么选举通过,那个从节点可以成为master
从节点执行主备切换,从节点切换为主节点
0 条评论
下一页