面试题答案
一键面试常见策略
- 先更新数据库,再更新缓存
- 操作步骤:应用程序先执行数据库的更新操作,成功后再更新Redis缓存。
- 优点:逻辑相对简单,易于理解和实现。
- 缺点:在高并发场景下,可能会出现缓存和数据库数据不一致问题。例如,线程A更新数据库后,线程B查询发现缓存数据不存在(可能是缓存过期),于是从数据库读取旧数据并写入缓存,紧接着线程A更新缓存,此时缓存中的数据就比数据库旧。
- 先删除缓存,再更新数据库
- 操作步骤:应用程序首先删除Redis缓存中的对应数据,然后执行数据库的更新操作。
- 优点:相对先更新数据库再更新缓存,在一定程度上减少了不一致的概率。因为删除缓存后,后续请求会从数据库读取最新数据并更新到缓存。
- 缺点:在高并发场景下,如果删除缓存后,更新数据库操作还未完成,另一个读请求过来,发现缓存不存在,会从数据库读取旧数据并写入缓存,此时会导致短暂的数据不一致。
- 先更新数据库,再删除缓存
- 操作步骤:应用程序先执行数据库的更新操作,成功后再删除Redis缓存。
- 优点:是目前较为常用的策略。因为数据库的写操作比缓存写操作更可靠,先确保数据库更新成功,再删除缓存,后续读请求会重新从数据库加载最新数据到缓存。
- 缺点:如果删除缓存失败,会导致缓存数据与数据库不一致。另外,在高并发场景下,也存在短暂的数据不一致窗口,例如更新数据库后,还未来得及删除缓存,读请求读取到旧的缓存数据。
可能遇到的问题及解决方案
- 缓存删除失败
- 问题描述:在采用先更新数据库再删除缓存策略时,数据库更新成功但缓存删除失败,导致缓存数据与数据库不一致。
- 解决方案:
- 重试机制:在缓存删除失败时,应用程序可以设置重试次数,多次尝试删除缓存。例如,使用Spring Retry框架进行重试配置。
- 异步删除:将缓存删除操作放入消息队列(如Kafka、RabbitMQ)中,由专门的消费者来处理缓存删除,这样即使删除操作失败,也可以在消息队列中记录下来,进行后续处理。同时确保消息的可靠投递和消费。
- 并发导致的数据不一致
- 问题描述:在高并发场景下,由于读写操作的顺序和时间差,可能会出现短暂的数据不一致。如上述先删除缓存再更新数据库场景中,读请求在缓存删除后、数据库更新前读取数据。
- 解决方案:
- 读写锁:对涉及到数据库和缓存操作的代码块加读写锁,读操作和写操作互斥,写操作和写操作也互斥。例如在Java中可以使用ReentrantReadWriteLock。但使用读写锁会降低系统并发性能。
- 延时双删:在采用先更新数据库再删除缓存策略时,删除缓存后,延迟一段时间再次删除缓存。延迟时间根据业务场景和系统性能来定,这样可以确保大部分读请求已经从数据库读取并更新缓存,再次删除缓存可以避免因缓存过期等原因导致旧数据重新写入缓存。
- 缓存雪崩
- 问题描述:当大量缓存数据同时过期,在高并发场景下,大量请求直接访问数据库,可能导致数据库压力过大甚至崩溃。
- 解决方案:
- 随机设置过期时间:在设置缓存过期时间时,采用随机值,避免大量缓存同时过期。例如原本设置缓存过期时间为60分钟,可以在50 - 70分钟之间随机取值。
- 二级缓存:使用两级缓存,如一级缓存为Redis,二级缓存可以是本地缓存(如Guava Cache)。当一级缓存失效时,先从二级缓存读取数据,减少对数据库的压力。同时对二级缓存设置合适的过期时间和淘汰策略。