如何保证缓存和数据库的双写一致性
1、最经典的缓存+数据库读写的模式,就是 Cache Aside Pattern。
(1)、读的时候,先读缓存,缓存没有的话,就读数据库,然后取出数据后放入缓存,同时返回响应
(2)、更新的时候先更新数据库,再去删除缓存
之所以是先删除缓存而不是去更新缓存就是为了避免大量的更新更新缓存操作所带来的资源消耗,并且大量被更新的数据不一定经常被使用到,也就是说缓存应该只是用到它的时候采取计算缓存,这样就可以避免大量的冷数据存储到缓存中去。
缓存不一致问题的解决方案:
1、最初级的缓存不一致问题及解决方案
场景::先更新数据库,再删除缓存。如果删除缓存失败了,那么会导致数据库中是新数据,缓存中却是旧数据,这就导致了数据库和缓存中的数据是不一致的情况.
解决方案:先删除缓存,再去更新数据库中的数据,就算数据库中的数据更新失败了也不会出现缓存与数据库数据不一致的情况,因为从缓存总查找不到对应的数据就会去数据库中查找。
2、在高并发情况下,数据库中未来得及修改的数据被缓存到了缓存中去,等到数据库数据更新成功之后便会出现缓存与数据库数据不一致的情况,这是第二种缓存与数据库数据不一致的情况。
解决方案:更新数据的时候,根据
数据的唯一标识 ,将操作路由之后,发送到一个 jvm 内部队列中。读取
数据的时候,如果发现数据,中存缓在不那么将重新执行“读取数据+更新缓存”的操作,根据唯一标识路由之后,也发送到同一个 jvm 内部队列中。一个队列对应一个工作线程,每个工作线程串行 拿到对应的操作,然后一条一条的执行。这样的话,一个数据变更的操作先删除缓存,然后再去更新数据库,但是还没完成更新。此时如果一个读请求过来,没有读到缓存,那么可以先将缓存更新的请求发送到队列中,此时会在队列中积压,然后同步等待缓存更新完成。这里有一个优化点 ,一个队列中,其实多个更新缓存请求串在一起是没意义的 ,因此可以做过滤,如果发现队列中已经有一个更新缓存的请求了,那么就不用再放个更新请求操作进去了,直接等待前面的更新操作请求完成即可。待那个队列对应的工作线程完成了上一个操作的数据库的修改之后,才会去执行下一个操作,也就是缓存更新的操作,此时会从数据库中读取最新的值,然后写入缓存中。如果请求还在等待时间范围内,不断轮询发现可以取到值了,那么就直接返回;如果请求等待的时间超过一定时长,那么这一次直接从数据库中读取当前的旧值。