1、Redis 怎么保证高可用、有哪些集群模式?
Redis 保证高可用可以通过:主从复制、哨兵模式、集群模式 。
① 主从复制② 哨兵
1)哨兵故障检测
在默认情况下, 会以每秒一次的频率向所有与它创建了命令连接的实例(包括主服务器、从服务器、其他在内)发送 PING 命令,并通过实例返回的 PING 命令回复来判断实例是否在线 。
如果一个实例在 down-after- 毫秒内,连续向返回无效回复,那么会修改这个实例所对应的实例结构,在结构的 flags 属性中设置标识,以此来表示这个实例已经进入主观下线状态 。
当将一个主服务器判断为主观下线之后,为了确定这个主服务器是否真的下线了,它会向同样监视这一服务器的其他进行询问 , 看它们是否也认为主服务器已经进入了下线状态(可以是主观下线或者客观下线) 。
当从其他那里接收到足够数量(,可配置)的已下线判断之后,就会将服务器置为客观下线,在 flags 上打上标识,并对主服务器执行故障转移操作 。
2)哨兵故障转移流程:
当哨兵监测到某个主节点客观下线之后,就会开始故障转移流程 。核心流程如下:
发起一次选举,选举出领头 领头在已下线主服务器的所有从服务器里面,挑选出一个从服务器,并将其升级为新的主服务器 。领头将剩余的所有从服务器改为复制新的主服务器 。领头更新相关配置信息,当这个旧的主服务器重新上线时,将其设置为新的主服务器的从服务器 。③ 集群模式
哨兵模式最大的缺点就是所有的数据都放在一台服务器上 , 无法较好的进行水平扩展 。
为了解决哨兵模式存在的问题,集群模式应运而生 。在高可用上,集群基本是直接复用的哨兵模式的逻辑,并且针对水平扩展进行了优化 。
2、Redis 事务的实现
一个事务从开始到结束通常会经历以下3个阶段:
1)事务开始:multi 命令将执行该命令的客户端从非事务状态切换至事务状态,底层通过 flags 属性标识 。
2)命令入队:当客户端处于事务状态时,服务器会根据客户端发来的命令执行不同的操作:
3)事务执行:当一个处于事务状态的客户端向服务器发送 exec 命令时,服务器会遍历事务队列,执行队列中的所有命令,最后将结果全部返回给客户端 。
不过 redis 的事务并不推荐在实际中使用,如果要使用事务,推荐使用 Lua 脚本,redis 会保证一个 Lua 脚本里的所有命令的原子性 。
3、Redis 如何实现分布式锁? ① 加锁
加锁通常使用 set 命令来实现,伪代码如下:
set key value PX milliseconds NX
几个参数的意义如下:
② 解锁
解锁需要两步操作:
由于当前 Redis 还没有原子命令直接支持这两步操作,所以当前通常是使用 Lua 脚本来执行解锁操作,Redis 会保证脚本里的内容执行是一个原子操作 。
脚本代码如下,逻辑比较简单:
if redis.call("get",KEYS[1]) == ARGV[1]thenreturn redis.call("del",KEYS[1])elsereturn 0end
两个参数的意义如下:
4、Redis 分布式锁过期了,还没处理完怎么办?
文章插图
为了防止死锁,我们会给分布式锁加一个过期时间,但是万一这个时间到了,我们业务逻辑还没处理完,怎么办?
首先,我们在设置过期时间时要结合业务场景去考虑,尽量设置一个比较合理的值,就是理论上正常处理的话 , 在这个过期时间内是一定能处理完毕的 。
之后,我们再来考虑对这个问题进行兜底设计 。
关于这个问题,目前常见的解决方法有两种:
守护线程“续命”:额外起一个线程,定期检查线程是否还持有锁 , 如果有则延长过期时间 。里面就实现了这个方案,使用“看门狗”定期检查(每1/3的锁时间检查1次),如果线程还持有锁,则刷新过期时间 。超时回滚:当我们解锁时发现锁已经被其他线程获取了,说明此时我们执行的操作已经是“不安全”的了,此时需要进行回滚,并返回失败 。5、缓存穿透
解决方案:
6、缓存击穿
解决方案:
使用 redis 分布式锁的伪代码,仅供参考:
public Object getData(String key) throws InterruptedException {Object value = http://www.kingceram.com/post/redis.get(key);// 缓存值过期if (value == null) {// lockRedis:专门用于加锁的redis;//"empty":加锁的值随便设置都可以if (lockRedis.set(key, "empty", "PX", lockExpire, "NX")) {try {// 查询数据库 , 并写到缓存 , 让其他线程可以直接走缓存value = http://www.kingceram.com/post/getDataFromDb(key);redis.set(key, value,"PX", expire);} catch (Exception e) {// 异常处理} finally {// 释放锁lockRedis.delete(key);}} else {// sleep50ms后,进行重试Thread.sleep(50);return getData(key);}}return value;}
7、缓存雪崩
解决方案:
8、布隆过滤器
布隆过滤器的特点是判断不存在的,则一定不存在;判断存在的,大概率存在,但也有小概率不存在 。并且这个概率是可控的,我们可以让这个概率变小或者变高,取决于用户本身的需求 。
布隆过滤器由一个和 一组 Hash 函数(算法)组成,是一种空间效率极高的概率型算法和数据结构,主要用来判断一个元素是否在集合中存在 。
在初始化时,的每一位被初始化为0,同时会定义 Hash 函数,例如有3组 Hash 函数:hash1、hash2、hash3 。
写入流程
当我们要写入一个值时,过程如下,以“”为例:
1)首先将“”跟3组 Hash 函数分别计算,得到的下标为:1、7、10 。
2)将的这3个下标标记为1 。
假设我们还有另外两个值:java 和 ,按上面的流程跟 3组 Hash 函数分别计算 , 结果如下:
java:Hash 函数计算下标为:1、7、11
:Hash 函数计算下标为:4、10、11
查询流程
当我们要查询一个值时 , 过程如下,同样以“”为例:
1)首先将“”跟3组 Hash 函数分别计算,得到的下标为:1、7、10 。
2)查看的这3个下标是否都为1,如果这3个下标不都为1,则说明该值必然不存在,如果这3个下标都为1,则只能说明可能存在 , 并不能说明一定存在 。
其实上图的例子已经说明了这个问题了,当我们只有值“”和“”时,下标为1的有:1、4、7、10、11 。
当我们又加入值“java”时 , 下标为1的还是这5个,所以当下标为1的为:1、4、7、10、11 时,我们无法判断值“java”存不存在 。
文章插图
其根本原因是,不同的值在跟 Hash 函数计算后,可能会得到相同的下标,所以某个值的标记位,可能会被其他值给标上了 。
这也是为啥布隆过滤器只能判断某个值可能存在,无法判断必然存在的原因 。但是反过来,如果该值根据 Hash 函数计算的标记位没有全部都为1 , 那么则说明必然不存在,这个是肯定的 。
降低这种误判率的思路也比较简单:
布隆过滤器的误判率还有专门的推导公式,有兴趣的可以去搜相关的文章和论文查看 。
9、如何保证数据库和缓存的数据一致性
由于数据库和缓存是两个不同的数据源,要保证其数据一致性,其实就是典型的分布式事务场景 , 可以引入分布式事务来解决,常见的有:2PC、TCC、MQ事务消息等 。
但是引入分布式事务必然会带来性能上的影响,这与我们当初引入缓存来提升性能的目的是相违背的 。
所以在实际使用中 , 通常不会去保证缓存和数据库的强一致性,而是做出一定的牺牲,保证两者数据的最终一致性 。
如果是实在无法接受脏数据的场景 , 则比较合理的方式是放弃使用缓存,直接走数据库 。
保证数据库和缓存数据最终一致性的常用方案如下:
1)更新数据库,数据库产生。
2)监听和消费 ,执行失效缓存操作 。
3)如果步骤2失效缓存失败 , 则引入重试机制,将失败的数据通过MQ方式进行重试 , 同时考虑是否需要引入幂等机制 。
兜底:当出现未知的问题时,及时告警通知,人为介入处理 。
人为介入是终极大法,那些外表看着光鲜艳丽的应用,其背后大多有一群苦逼的程序员,在不断的修复各种脏数据和bug 。
10、为什么是让缓存失效,而不是更新缓存
1)更新缓存
案例如下 , 有两个并发的写请求,流程如下:
分析:数据库中的数据是请求B的,缓存中的数据是请求A的,数据库和缓存存在数据不一致 。
2)失效(删除)缓存
案例如下,有两个并发的写请求,流程如下:
分析:由于是删除缓存,所以不存在数据不一致的情况 。
结论:通过上述案例,可以很明显的看出,失效缓存是更优的方式 。
文章参考:全网最硬核 Redis 高频面试题解析(2021年最新版)
总结的面试题也挺费时间的,文章会不定时更新,有时候一天多更新几篇,如果帮助您复习巩固了知识点,还请三连支持一下,后续会亿点点的更新!
【1、Redis 怎么保证高可用、有哪些集群模式?】为了帮助更多小白从零进阶 Java 工程师,从CSDN官方那边搞来了一套 《Java 工程师学习成长知识图谱》,尺寸 870mm x 560mm,展开后有一张办公桌大小,也可以折叠成一本书的尺寸,有兴趣的小伙伴可以了解一下,当然,不管怎样博主的文章一直都是免费的~
- 可以听的小说有哪些
- 黄油鸡蛋巧克力的做法大全 黄油鸡蛋巧克力的做法
- 草莓罐头冰淇淋的做法 冰镇草莓罐头
- 肉色怎么调 肉色怎么调出来 水粉
- cad小于等于号怎么打 小于等于号怎么打
- 节拍器怎么用 吉他节拍器怎么用
- 戴森球计划戴森壳怎么造 戴森球计划戴森球怎么造
- 地线怎么接插座 地线怎么接
- breezip怎么解压文件夹 breezip怎么解压文件
- 40的因数有哪些 32的因数有哪些