面试题答案
一键面试Redis缓存方案设计
- 商品详情页缓存
- 缓存策略:采用LFU(Least Frequently Used,最不经常使用)或LRU(Least Recently Used,最近最少使用)淘汰策略。由于商品详情页数据更新频率低,使用这两种策略能保证长时间不被访问的商品缓存被淘汰,为新的商品缓存腾出空间。
- 缓存数据结构:使用Hash结构存储商品详情信息,例如以商品ID作为Key,商品详情(如名称、描述、价格、图片等)作为Hash的Field - Value对。这样可以方便地对单个商品的详情进行读写操作。
- 缓存更新:当商品数据发生变化时,通过消息队列(如Kafka)异步更新Redis中的商品详情缓存。在商品更新逻辑完成后,向消息队列发送更新消息,消费者接收到消息后更新Redis缓存,以减少对业务主流程的影响。
- 购物车缓存
- 缓存策略:采用不淘汰策略,因为购物车数据实时性要求高,要保证购物车数据始终存在于缓存中。
- 缓存数据结构:使用Hash结构,以用户ID作为Key,购物车中的商品(商品ID - 数量)作为Hash的Field - Value对。这样可以快速定位和更新某个用户的购物车信息。
- 缓存更新:当用户对购物车进行操作(添加、删除商品,修改商品数量等)时,直接同步更新Redis中的购物车缓存,确保数据的实时性。同时,将购物车操作记录通过消息队列异步持久化到数据库,保证数据的最终一致性。
高并发场景下可能遇到的问题及解决方案
- 缓存击穿问题
- 问题描述:高并发情况下,一个热点商品的缓存过期瞬间,大量请求同时访问数据库,导致数据库压力骤增。
- 解决方案:
- 互斥锁:在缓存过期时,使用Redis的SETNX(SET if Not eXists)命令获取互斥锁,只有获取到锁的请求才能去查询数据库并更新缓存,其他请求等待。获取锁的请求更新完缓存后释放锁。
- 永不过期:对于热点商品设置一个超长的过期时间,如一年,同时在数据更新时主动更新缓存,避免因过期导致的击穿问题。
- 缓存雪崩问题
- 问题描述:大量缓存同时过期,导致大量请求直接访问数据库,使数据库不堪重负甚至崩溃。
- 解决方案:
- 随机过期时间:在设置缓存过期时间时,给每个缓存设置一个随机的过期时间,避免大量缓存同时过期。例如,原本设置1小时过期,可以设置在50 - 70分钟之间随机过期。
- 二级缓存:采用二级缓存架构,一级缓存使用Redis,二级缓存可以使用本地缓存(如Guava Cache)。当一级缓存失效时,先从二级缓存获取数据,若二级缓存也没有则查询数据库并更新两级缓存,减轻数据库压力。
- 缓存一致性问题
- 问题描述:在高并发读写场景下,可能出现数据库和缓存数据不一致的情况,比如先更新数据库成功,再更新缓存失败。
- 解决方案:
- 重试机制:当缓存更新失败时,通过重试机制多次尝试更新缓存,直到成功为止。可以设置最大重试次数和重试间隔时间。
- 异步补偿:将缓存更新失败的记录发送到消息队列,通过消息队列的消费者进行异步补偿操作,确保缓存最终能更新成功。
- 读写锁:在对数据进行读写操作时,使用读写锁保证同一时间只有一个写操作,读操作可以并发进行,避免读写冲突导致的数据不一致问题。