Cache Aside Pattern模式
读的时候
先读缓存,缓存没有的话,再读取数据库,然后取出数据后放入缓存,同时返回响应
更新的时候
先删除缓存,然后再更新数据库(之所以删除缓存而不是更新,其实是一个懒加载的思想,避免频繁更新,降低开销,同时也可以避免更新缓存成功后在更新数据库时异常带来的问题)
缓存、数据库双写不一致
场景1: 先修改数据库,再修改/删除缓存,如果修改/删除缓存失败了,导致数据库中是新数据,缓存中是旧数据,数据出现不一致
解决思路
先删除缓存,再修改数据库,如果修改数据库失败,那么数据库中是旧数据,缓存中是空的,也不会存在不一致的问题(因为读的时候缓存没有会去数据库中读取,然后更新到缓存中)
场景2: 在更新一个库存同时在读取库存的缓存时,由于并发此时出现数据库和缓存数据不一致的问题。例如库存初始为100,同时发生修改库存和查询库存两个请求,最终导致缓存中库存为100,数据库中库存为99,如下图:
解决思路
- 延时双删,更新完数据库之后,再sleep一段时间,然后再次删除缓存
- 程序中维护相应的内存队列,将请求按照一个标识值放到队列里,进行串行化操作
- 采用redis设置写锁同时设置失效时间避免死锁
- 使用zookeeper分布式锁
个人觉得如果系统不是严格要求缓存+数据库数据一致性的话,最好不要采用后面这三种方案,因为核心思路都是请求串行化,会导致系统吞吐量大幅度降低
总结
针对缓存不是更新,而应该是删除。删除缓存有两种方式:
- 先删除缓存,再更新数据库。解决方案是使用延迟双删。
- 先更新数据库,再删除缓存。解决方案是消息队列或者数据库binlog同步,需要注意的是引入消息队列会增加系统复杂性,不推荐直接使用