面试官:Redis了解过吗?知不知道它有哪些主要用途?
派大星:Redis是一个开源的内存数据结构存储系统,用于高性能应用程序。它的主要用途包括缓存、会话存储、消息队列、排行榜/计数器和分布式锁等。
- 缓存:将频繁访问的数据存储在Redis中,以加快读取速度。
- 会话存储:将用户会话数据存储在Redis中,实现可扩展和高性能的会话管理。
- 消息队列:利用Redis的发布/订阅功能,实现异步消息传递。
- 排行榜/计数器:使用Redis的有序集合和计数器功能,实现排行榜和计数器应用。
- 分布式锁:利用Redis的原子操作和过期时间特性,实现分布式锁机制。
面试官:不错。那么,作为缓存,Redis有哪些挑战和问题呢?聊一聊
派大星:主要的三大经典问题包括缓存击穿、缓存穿透和缓存雪崩,当然还有数据一致性、内存消耗和缓存更新的延迟等问题。
面试官:非常好。那么,针对缓存击穿、缓存穿透和缓存雪崩,你有什么具体的解决方案或思路吗?
派大星:首先聊一聊缓存击穿,所谓的缓存击穿主要原因是 key的过期造成并发访问数据库,也就是所有的请求打到数据库,如何阻止?做好数据库的最后一道防线,我们就可以按照这个思路去进行解决。
首先我们分析一下:因为reids是单进程,单实例,这样我们可以利用redis作为锁:setnx,只有获取到锁的去访问DB,这样就可以有效的防止请求打到服务器
大致步骤如下:
- getkey
- setnx() -锁
- ok的去数据库
- flase的sleep一会
面试官:那么这么做就可以了吗?
派大星:当然!!!不太可以。上述方案的也有弊端:就是会产生死锁,以及锁超时的问题
面试官:那应该如何解决死锁和锁超时的问题呢?
派大星:首先我们可以想到如果第一个抢到锁的挂了,可以通过设置过期时间解决死锁问题;同时虽然设置了过期时间但是有可能会产生抢到锁的并没有挂只是没有处理完,这是锁的过期时间已经达到了。可以通过在客户端使用两个线程进行监听来解决:一个线程去数据获取数据,另外一个线程监听是否从数据库中取出结果,从而更新锁的超时时间,从而解决锁超时问题。
注意:到这里其实代码复杂度就已经很高了。可以使用zk临时顺序节点做分布式锁。
面试官:那接下来聊一下缓存穿透的问题吧?
派大星:好的,所谓的缓存穿透指的是缓存和数据库都不存在的数据。我们可以通过bloom过滤器。
具体的解决方案可以参考如下方案:
- client实现算法和存储数据结构,client压力较大
- client实现算法,redis承载bloom的bitmap-无状态
- redis集成bloom(压力在redis,但更符合service mactc的设计理念,同时redis的承受能力要比client强)
面试官:不错不错,那雪崩的问题呢?
派大星:所谓的缓存雪崩也就是大量key失效导致请求直接到达DB。如果想要解决雪崩问题我们需要做如下分析:什么场景下会产生雪崩:也就是系统的零点过期。针对上述场景可以有两种解决方案
- 方案一:如果到达某一时刻必须过期(key),必须造成雪崩,解决方案就是强依赖击穿方案,或者业务判断,零点延时。
- 方案二:如果与时点性无关解决方案就是随机过期时间
面试官:非常好,你的回答非常详细。下次我们再聊聊数据一致性、内存消耗和缓存更新的延迟等问题。非常感谢你的时间和分享。
派大星:非常感谢您的提问,期待下一次的面试。