数据库和缓存如何保证一致性
- (实际采用)先更新数据库,成功后,再删除(失效)缓存:并发问题:比如,缓存中的数据刚好失效,导致读请求去数据库中读取了数据,而此时写操作更新了数据库,成功后又删除了缓存,但前面的那个读请求又将之前读取的旧数据存到了缓存,从而导致缓存中是脏数据了。。但这种情况实际出现的概率很低,因为这个条件需要发生在读缓存时缓存失效,而且并发着有一个数据库的写操作,同时读操作在写操作前进入数据库,而又要晚于写操作更新缓存。但实际上数据库的写操作会比读操作慢得多(写操作会加锁),所以这些条件都具备的概率很小。可以采用过期失效的方式来解决,就是设置过期时间,到期后缓存失效,载入最新的数据,但需要系统能够容忍一段时间的数据不一致。(对于实时性要求很高的数据,可以将数据放在数据库中,而不放到缓存里。数据库中的数据进行修改时会给数据加上写锁,于是可以避免缓存场景中的数据不一致问题。)
- 更新数据库成功,删除缓存失败怎么办?
- 将想要删除的key放进消息队列,消息队列的消费端收到这个key时就执行删除缓存中对应key的操作, 并设置失败重试(消息队列的请求确认机制),超过最大重试次数(比如5次)后就报警给运维人员。
-
先更新数据库,后更新缓存:在并发情况下会存在多线程 写缓存 造成脏数据的问题:假设A、B两个线程,A先更新数据库,B后更新数据库,然后AB分别更新缓存,但是B先更新缓存成功,A后更新缓存成功,这样就导致数据库是最新的数据但缓存中是旧的脏数据。而如果删除缓存数据的话,可以保证下一次读请求回源到数据库,将最新的数据载入到缓存中,避免脏数据的问题。因此,针对数据更新缓存,采用删除缓存的方式进行处理。
-
先删除缓存,再更新数据库:并发问题:删除缓存后,查询操作没有命中缓存,会把老数据读出来后放到缓存中,然后更新操作才去更新数据库。于是,在缓存中的数据还是旧数据,导致缓存中的数据是脏的,而且还一直这样脏下去了。
不能用事务
- 如果将更新数据库和删除缓存放到一个事务中,如果操作很耗时的话,就会造成大量的数据库连接挂起,严重降低系统的性能。