面试题答案
一键面试本地缓存数据一致性问题及解决
- 问题:
- 缓存与数据库不一致:当数据在数据库中更新后,本地缓存中的数据可能未及时更新,导致应用读取到旧数据。在高并发场景下,多个线程同时读取和更新数据,这种不一致情况更容易出现。
- 解决方法:
- 读写锁:使用读写锁来控制对缓存和数据库的访问。读操作可以并发执行,但写操作需要获取独占锁。当数据更新时,先获取写锁,更新数据库后立即更新本地缓存,然后释放锁。这样可以保证在数据更新期间,其他线程不会读取到旧数据。例如在Java中可以使用
ReentrantReadWriteLock
。 - 失效策略:设置合理的缓存失效时间。当数据更新时,直接使缓存失效,下次读取时从数据库重新加载数据到缓存。在高并发场景下,虽然可能出现短时间内缓存未命中的情况,但能保证数据的最终一致性。比如在Spring Cache中,可以设置
@Cacheable
注解的cacheManager
的失效时间属性。
- 读写锁:使用读写锁来控制对缓存和数据库的访问。读操作可以并发执行,但写操作需要获取独占锁。当数据更新时,先获取写锁,更新数据库后立即更新本地缓存,然后释放锁。这样可以保证在数据更新期间,其他线程不会读取到旧数据。例如在Java中可以使用
分布式缓存数据一致性问题及解决
- 问题:
- 缓存穿透:大量请求查询不存在的数据,这些请求绕过缓存直接访问数据库,可能导致数据库压力过大甚至崩溃。在高并发环境下,这种情况会迅速耗尽数据库资源。
- 缓存雪崩:缓存中大量数据同时过期,导致大量请求瞬间直接访问数据库,造成数据库负载过高甚至宕机。
- 缓存击穿:热点数据的缓存过期瞬间,大量并发请求同时访问该数据,这些请求直接打到数据库,可能使数据库压力骤增。
- 解决方法:
- 缓存穿透解决:
- 布隆过滤器:在缓存之前添加布隆过滤器,将所有可能存在的数据哈希到一个足够大的位数组中。当请求过来时,先通过布隆过滤器判断数据是否存在,如果不存在则直接返回,不会访问数据库。例如在Guava库中就提供了布隆过滤器的实现。
- 空值缓存:当查询数据库发现数据不存在时,也将空值缓存起来,并设置较短的过期时间,这样后续相同请求就不会再次访问数据库。
- 缓存雪崩解决:
- 随机过期时间:为缓存数据设置随机的过期时间,避免大量数据同时过期。例如原本设置过期时间为1小时,可以在50分钟到70分钟之间随机取值。
- 加锁排队:在缓存失效后,通过加分布式锁(如Redis的SETNX命令实现分布式锁),保证只有一个请求去数据库加载数据并更新缓存,其他请求等待,从而避免大量请求同时访问数据库。
- 缓存击穿解决:
- 互斥锁:和缓存雪崩中加锁排队类似,在热点数据缓存过期时,使用分布式锁,只有一个线程能去数据库加载数据并更新缓存,其他线程等待。
- 永不过期:对热点数据设置永不过期,在数据更新时同时更新缓存。可以通过后台线程定时去更新缓存数据,保证数据的实时性。
- 缓存穿透解决: