写点什么

Redis 面试题汇总,mysql 索引优化面试题常问

  • 2022 年 4 月 22 日
  • 本文字数:2747 字

    阅读完需:约 9 分钟


[](()slave 宕机

相对简单,slave 启动后会自动同步数据,增量同步。

[](()master 宕机

手动恢复


  1. 在从数据库中执行 SLAVEOFNO ONE 命令,断开主从关系并且将从库提升为主库继续服务;

  2. 将主库重新启动后,执行 SLAVEOF 命令,将其设置为其他库的从库,这时数据就能更新回来


哨兵功能自动恢复


通过 sentinel 模式启动 redis 后,自动监控 master/slave 的运行状态, 已经被集成在 redis2.4+的版本中如果 Master 异常,则会进行 Master-Slave 切换,将其中一个 Slave 作为 Master,将之前的 Master 作为 Slave


基本原理是:心跳机制+投票裁决


[](()6.缓存问题



[](()缓存穿透

缓存穿透是指查询一个一定不存在的数据,由于缓存是不命中时需要从数据库查询,查不到数据则不写入缓存,这将导致这个不存在的数据每次请求都要到数据库去查询,造成缓存穿透。


解决办法


  1. 对所有可能查询的参数以 hash 形式存储,在控制层先进行校验,不符合则丢弃。还有最常见的则是采用布隆过滤器,将所有可能存在的数据哈希到一个足够大的 bitmap 中,一个一定不存在的数据会被这个 bitmap 拦截掉,从而避免了对底层存储系统的查询压力。

  2. 也可以采用一个更为简单粗暴的方法,如果一个查询返回的数据为空(不管是数据不存在,还是系统故障),我们仍然把这个空结果进行缓存,但它的过期时间会很短,最长不超过五分钟。

[](()缓存雪崩

如果缓存集中在一段时间内失效,发生大量的缓存穿透,所有的查询都落在数据库上,造成了缓存雪崩。


解决办法


  1. 在缓存失效后,通过加锁或者队列来控制读数据库写缓存的线程数量。比如对某个 key 只允许一个线程查询数据和写缓存,其他线程等待。

  2. 可以通过缓存 reload 机制,预先去更新缓存,再即将发生大并发访问前手动触发加载缓存

  3. 不同的 key,设置不同的过期时间,让缓存失效的时间点尽量均匀. 比如我们可以在原有的失效时间基础上增加一个随机值,比如 1-5 分钟随机,这样每一个缓存的过期时间的重复率就会降低,就很难引发集体失效的事件

  4. 做二级缓存,或者双缓存策略。A1 为原始缓存,A2 为拷贝缓存,A1 失效时,可以访问 A2,A1 缓存失效时间设置为短期,A2 设置为长期。

[](()缓存击穿

缓存被“击穿”的问题,这个和缓存雪崩的区别在于这里针对某一 key 缓存,前者则是很多 key。

[](()缓存预热

缓存预热就是系统上线后,提前将相关的缓存数据直接加载到缓存系统。避免在用户请求的时候,先查询数据库,然后再将数据缓存的问题!用户直接查询事先被预热的缓存数据!


缓存预热解决方案


  1. 直接写个缓存刷新页面,上线时手工操作下;

  2. 数据量不大,可以在项目启动的时候自动进行加载;

  3. 定时刷新缓存;

[](()缓存更新

我们知道通过 expire 来设置 key 的过期时间,那么对过期的数据怎么处理呢?除了缓存服务器自带的缓存失效策略之外(Redis 默认的有 6 中策略可供选择),我们还可以根据具体的业务需求进行自定义的缓存淘汰,常见的策略有两种:


  1. 定时去清理过期的缓存;

  2. 当有用户请求过来时,再判断这个请求所用到的缓存是否过期,过期的话就去底层系统得到新数据并更新缓存。


两者各有优劣,第一种的缺点是维护大量缓存的 key 是比较麻烦的,第二种的缺点就是每次用户请求过来都要判断缓存失效,逻辑相对比较复杂!具体用哪种方案,大家可以根据自己的应用场景来权衡。

[](()缓存降级

当访问量剧增、服务出现问题(如响应时间慢或不响应)或非核心服务影响到核心流程的性能时,仍然需要保证服务还是可用的,即使是有损服务。系统可以根据一些关键数据进行自动降级,也可以配置开关实现人工降级。


*降级的最终目的 Java 开源项目【ali1024.coding.net/public/P7/Java/git】 *是保证核心服务可用,即使是有损的。而且有些服务是无法降级的(如加入购物车、结算)。


在进行降级之前要对系统进行梳理,看看系统是不是可以丢卒保帅;从而梳理出哪些必须誓死保护,哪些可降级;比如可以参考日志级别设置预案:


  1. 一般:比如有些服务偶尔因为网络抖动或者服务正在上线而超时,可以自动降级;

  2. 警告:有些服务在一段时间内成功率有波动(如在 95~100%之间),可以自动降级或人工降级,并发送告警;

  3. 错误:比如可用率低于 90%,或者数据库连接池被打爆了,或者访问量突然猛增到系统能承受的最大阀值,此时可以根据情况自动降级或者人工降级;

  4. 严重错误:比如因为特殊原因数据错误了,此时需要紧急人工降级。

[](()缓存热点 key

使用缓存 + 过期时间的策略既可以加速数据读写,又保证数据的定期更新,这种模式基本能够满足绝大部分需求。但是有两个问题如果同时出现,可能就会对应用造成致命的危害:


  1. 当前 key 是一个热点 key( 可能对应应用的热卖商品、热点新闻、热点评论等),并发量非常大。

  2. 重建缓存不能在短时间完成,可能是一个复杂计算,例如复杂的 SQL、多次 IO、多个依赖等。


在缓存失效的瞬间,有大量线程来重建缓存,造成后端负载加大,甚至可能会让应用崩溃。


热点 key 失效后大量线程重建缓存


要解决这个问题也不是很复杂,但是不能为了解决这个问题给系统带来更多的麻烦,所以需要制定如下目标:


减少重建缓存的次数


数据尽可能一致


较少的潜在危险


1)互斥锁 (mutex key)


此方法只允许一个线程重建缓存,其他线程等待重建缓存的线程执行完,重新从缓存获取数据即可,


下面代码使用 Redis 的 setnx 命令实现上述功能,伪代码:


String get(String key) {


//从 redis 中获取 key


String value = redis.get(key);


//如果 value 为空则开始重构缓存


if (value == null) {


//只允许一个线程重构缓存,使用 nx,并设置过期时间 ex


String mutexKey = "mutex:key" + key;


if (redis.set(mutexKey, "1", "ex 180", "nx")) {


//从数据源获取数据


value = db.get(key);


//回写 redis 并设置过期时间


redis.set(key, value, timeout);


//删除 mutexKey


redis.del(mutexKey);


} else {


//其他线程睡眠 50 秒再重试


Thread.sleep(50); get(key);


}


}


return value;


}


从 Redis 获取数据,如果值不为空,则直接返回值。


如果 set(nx 和 ex) 结果为 true,说明此时没有其他线程重建缓存,那么当前线程执行缓存构建逻辑。


如果 setnx(nx 和 ex) 结果为 false,说明此时已经有其他线程正在执行构建缓存的工作,那么当前线程将休息指定时间 (例如这里是 50 毫秒,取决于构建缓存的速度 ) 后,重新执行函数,直到获取到数据。


2)永远不过期


永远不过期”包含两层意思:


从缓存层面来看,确实没有设置过期时间,所以不会出现热点 key 过期后产生的问题,也就是“物理”不过期。


从功能层面来看,为每个 value 设置一个逻辑过期时间,当发现超过逻辑过期时间后,会使用单独的线程去构建缓存。


从实战看,此方法有效杜绝了热点 key 产生的问题,但唯一不足的就是重构缓存期间,会出现数据不一致的情况,这 《一线大厂 Java 面试题解析+后端开发学习笔记+最新架构讲解视频+实战项目源码讲义》开源 取决于应用方是否容忍这种不一致。下面代码使用 Redis 进行模拟:

最后

学习视频:



大厂面试真题:



用户头像

还未添加个人签名 2022.04.13 加入

还未添加个人简介

评论

发布
暂无评论
Redis面试题汇总,mysql索引优化面试题常问_Java_爱好编程进阶_InfoQ写作社区