分布式缓存redis中
1 redis的持久化有哪几种方式?不同的持久化机制都有什么优缺点?持久化机制具体底层是如何实现的?
(1) 为什么要做持久化?
redis持久化的意义,在于故障恢复
(2) RDB和AOF两种持久化机制
RDB:RDB持久化的方式就是对redis中的数据执行周期性生成一个RDB文件,可以通过加载RDB文件来快速重新构建内存数据 ,事实上redis主从结构中的全量复制就是这样做的。
AOF:AOF机制对每条写入命令作为日志,以append-only的模式写入一个日志文件中,在redis重启的时候,可以通过回放AOF日志中的写入指令来重新构建整个数据集。
通过RDB和AOF,我们可以将备份文件备份到云服务上去 比如阿里云,如果redis挂了,可以从云服务上拷贝回来之前的数据,放到指定的目录中,然后重新启动redis,redis就会自动根据持久化数据文件中的数据,去恢复内存中的数据,继续对外提供服务。如果同时使用RDB和AOF两种持久化机制,那么在redis重启的时候,会使用AOF来重新构建数据,因为AOF中的数据更加完整。
文章图片
RDB与AOF的机制
(3)两种模式的优缺点
1 RDB持久化的优点
- RDB方式适合做冷备份,每个rdb文件都代表每一时刻redis的数据快照,这种方式适合做冷备,可以将这些文件放到云服务上去,便于恢复。AOF也可以做冷备,但是因为AOF只有一个文件,这个时候我们需要写定时任务脚本来copy各个时刻AOF的文件。自己写脚本 肯定就麻烦咯。
- 使用RDB的时候,redis对外读写服务影响比较小,因为这种方式是redis主进程fork一个子进程来做持久化的。而AOF是主进程读写AOF文件的。
- RDB文件上存放的是redis的各种指令集,AOF文件存放的是redis命令,相较于命令,指令集的恢复速度要快的多 这也是RDB的优势。
- 如果想要在redis故障时,尽可能少的丢失数据,那么RDB没有AOF好。一般来说,RDB数据快照文件,都是每隔5分钟,或者更长时间生成一次,这个时候就得接受一旦redis进程宕机,那么会丢失最近5分钟的数据
- RDB每次在fork子进程来执行RDB快照数据文件生成的时候,如果数据文件特别大,可能会导致对客户端提供的服务暂停数毫秒,或者甚至数秒
文章图片
image.png
3 AOF持久化机制的优点
- AOF可以更好的保护数据不丢失,一般AOF会每隔1秒,通过一个后台线程执行一次fsync操作,最多丢失1秒钟的数据
- AOF日志文件即使过大的时候,出现后台重写操作,也不会影响客户端的读写。因为在rewrite log的时候,会对其中的指导进行压缩,创建出一份需要恢复数据的最小日志出来。再创建新日志文件的时候,老的日志文件还是照常写入。当新的merge后的日志文件ready的时候,再交换新老日志文件即可。
- AOF日志文件的命令通过非常可读的方式进行记录,这个特性非常适合做灾难性的误删除的紧急恢复。比如某人不小心用flushall命令清空了所有数据,只要这个时候后台rewrite还没有发生,那么就可以立即拷贝AOF文件,将最后一条flushall命令给删了,然后再将该AOF文件放回去,就可以通过恢复机制,自动恢复所有数据
文章图片
image.png
4 AOF持久化机制的缺点
- 对于同一份数据来说,AOF日志文件通常比RDB数据快照文件更大
- AOF开启后,支持的写QPS会比RDB支持的写QPS低,因为AOF一般会配置成每秒fsync一次日志文件,当然,每秒一次fsync,性能也还是很高的
- 以前AOF发生过bug,就是通过AOF记录的日志,进行数据恢复的时候,没有恢复一模一样的数据出来。所以说,类似AOF这种较为复杂的基于命令日志/merge/回放的方式,比基于RDB每次持久化一份完整的数据快照文件的方式,更加脆弱一些,容易有bug。不过AOF就是为了避免rewrite过程导致的bug,因此每次rewrite并不是基于旧的指令日志进行merge的,而是基于当时内存中的数据进行指令的重新构建,这样健壮性会好很多。
(4) RDB和AOF到底该如何选择 - 不要仅仅使用RDB,因为那样会导致你丢失很多数据
- 也不要仅仅使用AOF,因为那样有两个问题,第一,你通过AOF做冷备,没有RDB做冷备,来的恢复速度更快; 第二,RDB每次简单粗暴生成数据快照,更加健壮,可以避免AOF这种复杂的备份和恢复机制的bug
- 综合使用AOF和RDB两种持久化机制,用AOF来保证数据不丢失,作为数据恢复的第一选择; 用RDB来做不同程度的冷备,在AOF文件都丢失或损坏不可用的时候,还可以使用RDB来进行快速的数据恢复
在之前的主从架构下,redis所能容纳的数据量是主节点的最大值,这其实也就类似于高可用版本的单机。这个时候就需要集群来横向扩容了。
(2) redis cluster vs. replication + sentinal
如果数据量很少,主要是承载高并发高性能的场景,比如你的缓存一般就几个G,单机足够了
replication,一个mater,多个slave,要几个slave跟你的要求的读吞吐量有关系,然后自己搭建一个sentinal集群,去保证redis主从架构的高可用性,就可以了
redis cluster,主要是针对海量数据+高并发+高可用的场景,海量数据,如果你的数据量很大,那么建议就用redis cluster
(3)数据分布算法:hash , 一致性hash,redis cluster的hash slot
- hash算法 (具有大量的问题)
文章图片
image.png - 【分布式缓存redis中】一致性hash算法(自动缓存迁移)+虚拟节点(自动负载均衡)
文章图片
image.png
文章图片
虚拟节点 - redis cluster的hash slot算法
redis cluster有固定的16384个hash slot,对每个key计算CRC16值,然后对16384取模,可以获取key对应的hash slot。redis cluster中每个master都会持有部分slot,比如有3个master,那么可能每个master持有5000多个hash slot。hash slot让node扩容和缩容简单,扩容缩容只需要迁移slot即可了,移动hash slot的成本是非常低的。
(4)redis cluster的核心原理
(1)redis cluster节点间采取gossip协议进行通信
节点通信一般有两种方式:基于zk的集中管理,分布式管理(redis用的就是这种 叫gossip协议)。
分布式管理跟集中式不同,不是将集群元数据(节点信息,故障,等等)集中存储在某个节点上,而是互相之间不断通信,保持整个集群所有节点的数据是完整的
集中式:好处在于,元数据的更新和读取,时效性非常好,一旦元数据出现了变更,立即就更新到集中式的存储中,其他节点读取的时候立即就可以感知到; 不好在于,所有的元数据的跟新压力全部集中在一个地方,可能会导致元数据的存储有压力
gossip:好处在于,元数据的更新比较分散,不是集中在一个地方,更新请求会陆陆续续,打到所有节点上去更新,有一定的延时,降低了压力; 缺点,元数据更新有延时,可能导致集群的一些操作会有一些滞后
(2)10000端口
每个节点都有一个专门用于节点间通信的端口,就是自己提供服务的端口号+10000,比如7001,那么用于节点间通信的就是17001端口,每隔节点每隔一段时间都会往另外几个节点发送ping消息,同时其他几点接收到ping之后返回pong。
(3)gossip协议
文章图片
image.png
面向集群的jedis内部实现原理
1 基于重定向的客户端
(1)redis-cli -c,自动重定向
客户端可能会挑选任意一个redis实例去发送命令,每个redis实例接收到命令,都会计算key对应的hash slot,如果在本地就在本地处理,否则返回moved给客户端,让客户端进行重定向,用redis-cli的时候,可以加入-c参数,支持自动的请求重定向,redis-cli接收到moved之后,会自动重定向到对应的节点执行命令。
(2)计算hash slot
计算hash slot的算法,就是根据key计算CRC16值,然后对16384取模,拿到对应的hash slot,用hash tag可以手动指定key对应的slot,同一个hash tag下的key,都会在一个hash slot中,比如set mykey1:{100}和set mykey2:{100}。
2 smart jedis
(1)基于重定向的客户端,很消耗网络IO,因为大部分情况下,可能都会出现一次请求重定向,才能找到正确的节点,所以大部分的客户端,比如java redis客户端,就是jedis,都是smart的。
smart 重定向的原理是:本地维护一份hashslot -> node的映射表,缓存,大部分情况下,直接走本地缓存就可以找到hashslot -> node,不需要通过节点进行moved重定向
hashslot迁移和ask重定向
(2)JedisCluster的工作原理
在JedisCluster初始化的时候,就会随机选择一个node,初始化hashslot -> node映射表,同时为每个节点创建一个JedisPool连接池
每次基于JedisCluster执行操作,首先JedisCluster都会在本地计算key的hashslot,然后在本地映射表找到对应的节点
如果那个node正好还是持有那个hashslot,那么就ok; 如果说进行了reshard(重新分片)这样的操作,可能hash slot已经不在那个node上了,就会返回moved,如果JedisCluter API发现对应的节点返回moved,那么利用该节点的元数据,更新本地的hashslot -> node映射表缓存,重复上面几个步骤,直到找到对应的节点,如果重试超过5次,那么就报错JedisClusterMaxRedirectionException。
(3)hashslot迁移和ask重定向
如果hash slot正在迁移,那么会返回ask重定向给jedis,jedis接收到ask重定向之后,会重新定位到目标节点去执行,但是因为ask发生在hash slot迁移过程中,所以JedisCluster API收到ask是不会更新hashslot本地缓存,已经可以确定说,hashslot已经迁移完了,moved是会更新本地hashslot->node映射表缓存的。
redis cluster的高可用的原理,几乎跟哨兵是类似的,应该是cluster直接聚合了sentinal。
推荐阅读
- 不废话,代码实践带你掌握|不废话,代码实践带你掌握 强缓存、协商缓存!
- 深入浅出谈一下有关分布式消息技术(Kafka)
- 15、IDEA学习系列之其他设置(生成javadoc、缓存和索引的清理等)
- springboot使用redis缓存
- 缓存有关的配置和属性
- (1)redis集群原理及搭建与使用(1)
- KubeDL HostNetwork(加速分布式训练通信效率)
- springboot结合redis实现搜索栏热搜功能及文字过滤
- Redis——发布订阅/消息队列
- redis|redis 常见问题一