前言: 从Redis 3.0发布提供Redis Cluster以后,经历Redis 4.x、Redis5.x和Redis 6.x一系列版本,Redis Cluster更加成熟、稳定,推荐企业使用此种架构,通常公司也是使用此种架构。如果使用Redis Cluster集群,面试中碰到的问题有一些坑,还望注意。
1.缓存穿透、缓存击穿、 缓存雪崩
简介: 在应用程序和MySQL数据库中建立一个中间层:Redis缓存,通过Redis缓存可以有效减少查询数据库的时间消耗,但是引入redis又有可能出现缓存穿透、缓存击穿、缓存雪崩等问题。
将redis和mysql穿透了,他们里面都没有请求的数据。
问题:持续大量的消耗mysql性能。
解决:将空数据也存入redis中。
将redis穿透了(没有数据),mysql中有数据。
问题:在失效后,重新放入缓存的过程中,mysql读取较慢,会出现瞬间大量的消耗mysql性能。
解决:加锁。setnx:第一次返回1,读mysql;后续请求返回0,等待redis。
String get(String key) {
String value = redis.get(key);
if (value == null) {
// 如果key不存在,则设置为1
if (redis.setnx(key_mutex, "1")) {
// 设置key的过期时间为3分钟
redis.expire(key_mutex, 3 * 60)
// 从db中加载数据,但注意:只有一个线程能进入到这里,其他线程访问的时候已有课key_mutex
value = db.get(key);
// 从数据库中加载成功,则设置对应的数据
redis.set(key, value);
redis.delete(key_mutex);
} else {
//其他线程休息50毫秒后重试
Thread.sleep(50);
get(key);
}
}
}
整个山都崩了:大量的key失效。(故障重启、同时过期)
问题:瞬间大量请求进入mysql。
解决:消息队列(流量削峰)、key过期时间分散开避免压力集中。
2.数据类型
String 字符串类型
hash 类似于Map<String,String>
list 有序 可重复
set 无序 唯一
zset(sorted set) 带分数的set score保证有序 set保证唯一
3. Redis的持久化了解吗?
1. RDB 持久化:
① 在指定的时间间隔内持久化
② 服务 shutdown 会自动持久化
③ 输入 bgsave 也会持久化
2)AOF : 以日志形式记录每个更新操作
Redis 重新启动时读取这个文件,重新执行新建、修改数据的命令恢复数据。
保存策略:推荐(并且也是默认everysec)的措施为每秒持久化一次,这种策略可以兼顾速度和安全性。
缺点:
1 比起 RDB 占用更多的磁盘空间
2 恢复备份速度要慢
3 每次读写都同步的话,有一定的性能压力
4 存在个别 Bug,造成恢复不能
选择策略:
官方推荐:
string : 字符串
list: 可以重复的集合
set: 不可以重复的集合
hash :类似于 Map<String,String>
zset(sorted set): 带分数的 set
如果对数据不敏感,可以选单独用 RDB;不建议单独用 AOF,因为可能出现 Bug;如果只是做纯内存缓存,可以都不用
4. redis 是单线程的,为什么那么快
1)完全基于内存,绝大部分请求是纯粹的内存操作,非常快速。
2)数据结构简单,对数据操作也简单,Redis 中的数据结构是专门进行设计的
3)采用单线程,避免了不必要的上下文切换和竞争条件,也不存在多进程或者多线程
导致的切换而消耗 CPU,不用去考虑各种锁的问题,不存在加锁释放锁操作,没有因
为可能出现死锁而导致的性能消耗
4)使用多路 I/O 复用模型,非阻塞 IO
5. 悲观锁和乐观锁
悲观锁:执行操作前假设当前的操作肯定(或有很大几率)会被打断(悲观)。基于
这个假设,我们在做操作前就会把相关资源锁定,不允许自己执行期间有其他操作干
扰。
乐观锁:执行操作前假设当前操作不会被打断(乐观)。基于这个假设,我们在做操
作前不会锁定资源,万一发生了其他操作的干扰,那么本次操作将被放弃。Redis 使
用的就是乐观锁。
6. Redis的多数据库机制,了解多少?
7. 懂Redis的批量操作么?
8. Redis集群机制中,你觉得有什么不足的地方吗?
Keys * 查询键出错