郑州网站建设电话,竞价网站模板,什么是企业网站,动态视频素材网站概述
当我们在使用缓存时#xff0c;如果发生数据变更#xff0c;那么你需要同时操作缓存和数据库#xff0c;而它们两个又分属不同的系统#xff0c;因此无法做到同时操作成功或失败#xff0c;因此在并发读写下很可能出现缓存与数据库数据不一致的情况
理论上可以通过…概述
当我们在使用缓存时如果发生数据变更那么你需要同时操作缓存和数据库而它们两个又分属不同的系统因此无法做到同时操作成功或失败因此在并发读写下很可能出现缓存与数据库数据不一致的情况
理论上可以通过分布式事务保证同时操作成功或失败但这会影响系统性能一般很少使用。虽然没办法做到缓存和数据库强一致但我们可以让他们的数据尽可能在绝大部分时间内保持一致并保证最终是一致的 缓存更新设计
一般来说都是采用删除缓存的方式更新缓存这就涉及到先删除缓存还是先更新数据库的顺序问题了
1. 先删除缓存后更新数据库
先删除缓存后更新数据库如果数据库没有更新成功下次读缓存发现不存在则从数据库读取并重建缓存此时数据库和缓存依旧保持一致但还是旧值
高并发下假设有两个线程并发读写数据可能会发生以下场景
线程 A 要更新 X 2原值 X 1线程 A 先删除缓存线程 B 读缓存发现不存在从数据库中读取到旧值X 1线程 A 将新值写入数据库X 2线程 B 将旧值写入缓存X 1最终 X 的值在缓存中是 1旧值在数据库中是 2新值发生不一致
可见在高并发下这种方式容易出现长时间的脏数据一般不建议使用
2. 先更新数据库后删除缓存
先更新数据库后删除缓存如果缓存没有删除成功数据库是最新值缓存中是旧值会发生不一致
再看两个线程并发读写数据
某一时刻缓存中 X 失效不存在数据库 X 1线程 A 读取数据库得到旧值X 1线程 B 更新数据库X 2)线程 B 删除缓存线程 A 将旧值写入缓存X 1最终 X 的值在缓存中是 1旧值在数据库中是 2新值发生不一致
这种方式依旧会出现数据不一致但概率很低所以普遍采用这种方式 更多优化
通过前面分析我们采用了先更新数据库再删除缓存的方式还可以进一步优化
1. 保证两步都执行成功
前面提到无论采用哪种方式只要第二步失败都会有问题所以我们需要保证第二步成功执行
一种简单的办法是失败就重试但这会占用资源并且立即重试大概率还是失败所以可以采用异步重试就是把重试请求写到消息队列由专门的消费者来重试直到成功
或者更直接的做法为了避免第二步执行失败我们可以把操作缓存这一步直接放到消息队列中由消费者来操作缓存这样做的好处是即使系统重启了消息也不会丢失
也可以通过订阅数据库变更日志再操作缓存的方式以 MySQL 举例当一条数据发生修改时MySQL 就会产生一条变更日志Binlog我们可以订阅这个日志拿到具体操作的数据然后再根据这条数据去删除对应的缓存。订阅变更日志目前也有了比较成熟的开源中间件例如阿里的 canal
2. 延迟双删
一般数据库会使用【主从复制 读写分离】提高性能这种情况下也有可能出现数据不一致
线程 A 更新主库 X 2原值 X 1线程 A 删除缓存线程 B 查询缓存没有命中查询「从库」得到旧值从库 X 1从库「同步」完成主从库 X 2线程 B 将「旧值」写入缓存X 1最终 X 的值在缓存中是 1旧值在主从库中是 2新值也发生不一致
解决办法就是延时双删比如线程 A 在更新数据库并删除缓存后延迟一段时间再删除一次延迟时间取决于主从复制的延迟时间一般凭经验估算 1s - 5s 左右