尚硅谷雷神课件:
方案一:先更新数据库再更新缓存,性能不如删除缓存好,比如一个数据在一个小时内更新了一千次,那么缓存也更新一千次,但是如果使用删除模式就只需要删除一次,等到需要获取数据的时候才把数据从数据库中加载到缓存中一次。
方案二:先更新数据库再删除缓存,可能导致没更新完数据库的时候其他请求就读取了旧缓存造成数据不一致,解决方案可以用消息队列中间价帮助,实时性一致性有所提升
针对先写数据库再删除缓存出现数据不一致问题进行优化
可以借助监听binlog的消息队列来做删除缓存的操作。例如阿里开源的canal。这样做的好处是,更新完数据库数据之后,代码中不需要对缓存中的数据进行处理,中间件帮你做了解耦,同时,中间件的这个东西本身就保证了高可用。
看熬丙推文中写的方案三: 先删除缓存再更新数据库
先删除缓存,数据库还没有更新成功,此时如果读取缓存,缓存不存在,去数据库中读取到的是旧值,缓存不一致发生。
解决方案可以使用延时双删:
为了避免更新数据库的时候,其他线程从缓存中读取不到数据,就在更新完数据库之后,再sleep一段时间,然后再次删除缓存。sleep的时间要对业务读写缓存的时间做出评估,sleep时间大于读写缓存的时间即可。
流程如下:
-
线程1删除缓存,然后去更新数据库
-
线程2来读缓存,发现缓存已经被删除,所以直接从数据库中读取,这时候由于线程1还没有更新完成,所以读到的是旧值,然后把旧值写入缓存
-
线程1,根据估算的时间,sleep,由于sleep的时间大于线程2读数据+写缓存的时间,所以缓存被再次删除
-
如果还有其他线程来读取缓存的话,就会再次从数据库中读取到最新值
删除缓存有两种方式:
-
先删除缓存,再更新数据库。解决方案是使用延迟双删。
-
先更新数据库,再删除缓存。解决方案是消息队列或者其他binlog同步,引入消息队列会带来更多的问题,并不推荐直接使用。
针对缓存一致性要求不是很高的场景,那么只通过设置超时时间就可以了。
其实,如果不是很高的并发,无论你选择先删缓存还是后删缓存的方式,都几乎很少能产生这种问题。综合雷神和熬丙的推文,缓存一致性问题应该是没有十全十美的解决方案,强一致性和性能只能自己平衡。