Redis在项目开发中起着举足轻重的作用,也是互联网公司面试必考的点,下面的灵魂8问,不知道你能答得上来几个?
基础功能
- 为什么使用Redis?
- 使用Redis有什么缺点?
- 单线程的Redis为什么这么快?
- Redis的数据类型及使用场景
- Redis过期策略和内存淘汰机制?
- Redis和数据库双写一致性问题
- 如何应对缓存穿透和缓存雪崩问题
- 如果解决Redis并发竞争key问题?
为什么使用Redis?
项目中使用Redis,主要考虑性能和并发。
在高并发的情况下,如果所有请求直接打到数据库,数据库连接数会很快用完,出现连接异常。
如果用Redis,可以做一层缓存,请求先访问Redis缓存,缓冲命中的情况下,直接返回了,不需要请求数据库,缓冲未命中才会请求数据库,并把请求结果缓存到Redis,提高了程序的性能和并发。
使用Redis有什么缺点?
使用Redis可以解决高并发问题,但也会引入新的问题。
- 缓存和数据库双写一致性问题
- 缓存雪崩问题
- 缓存击穿问题
- 缓存的并发竞争问题
单线程的Redis为什么这么快?
单线程工作模型,基于内存
- 纯内存操作
- 核心是基于非阻塞的IO多路复用机制
- C语言实现
- 单线程,避免了多线程的频繁上下文切换问题,预防了多线程可能产生的竞争问题。
Redis的数据类型及使用场景
String: 存储计数或字符串
Hash: 结构体信息
List: 消息队列
Set: 去重
SortedSet: 排行榜应用
Redis过期策略和内存淘汰机制?
Redis采用的是定期删除+惰性删除策略。
Redis和数据库双写一致性问题
最终一致性和强一致性
redis无法和数据库做到强一致性
最终一致性:
- 先删缓存,再更新数据库(客户端可能读到的是旧的数据,因为删完缓存后,还没有写入数据库,但是有新的请求读到了旧的数据,导致缓存里也是旧的数据)
- 先更新数据库,再删缓存(更新数据库后,有请求读到了缓存里的旧数据,导致数据不一致,删除缓存后,再读到的数据就是一致的了)
- 先删缓存,再更新数据库,再删缓存(同第一步,所以二次删缓存,保证最终一致性)
如何应对缓存穿透和缓存雪崩问题
缓存雪崩
redis服务器意外宕机,导致redis不可用,所有请求打到数据库,数据库连接数上升达到上限,会出现数据库连接异常,影响业务,即使redis恢复,可能redis里没有数据,还是瞬间打满数据库连接,引起缓存雪崩问题。
解决方案:
- 事前:Redis建立高可用方案,主从复制+哨兵+集群,保证不会全部出现宕机
- 事中:开启限流/降级机制,保证最大连接数不超过数据库承受的连接数
- 事后: 开启Redis缓存,保证redis重启后,redis恢复缓存数据
缓存穿透
黑客通过模拟大量不存在的key请求系统,因为在缓存里找不到数据,就会去数据库里查询,也会导致连接数上升,浪费资源
解决方案:
- 校验key的合法性(如果用了mongodb的_id,可以校验mongodb的合法性)
- 对于不存在的key,即是在数据库里没有查到数据,可以写一个默认值到缓存里并设立TTL,业务上别忘了处理这种情况
- 布隆过滤器,优先过滤是否存在key
缓存击穿
高并发中,缓存的key正好失效,导致大量的请求打到数据库
解决方案:
- 分布式锁,拿数据的时候锁住,会影响性能
- 后台线程在缓存失效前刷新缓存
- 如果key数据永不改变,那么把key设置为永不过期
热点数据集中失效问题
解决方案:
- 设置key的过期时间时,在基础时间上加一个指定范围的随机数
如果解决Redis并发竞争key问题?
参考: https://github.com/doocs/advanced-java/blob/master/docs/high-concurrency/redis-cas.md
- 分布式锁,同一时间,只能由一个人来写入,其他人都不允许读和写
高级功能
Redis的线程模型
Redis集群
参考:https://github.com/doocs/advanced-java/blob/master/docs/high-concurrency/redis-cluster.md