缓存数据一致性

主要参考自 美团点评 张韩文章: https://maimai.cn/article/detail?fid=1057432698&efid=vnXzd0cl0pDwUW6f6iMpGA&from=groupmessage

为什么会有缓存数据一致性问题(Why)

数据变更

更新完数据库,是更新缓存呢,还是删除缓存。

又或者是先删除缓存,再更新数据库?

总结为下面四种更新策略:

更新策略:

  1. 先更新数据库,再更新缓存
  2. 先删除缓存,再更新数据库
  3. 先更新数据库,再删除缓存
  4. 先更新缓存,再更新数据库(更新缓存成功,更新数据库失败,导致业务数据不一致。直接淘汰。)                                 

以上四种策略分析见脑图如下:

电商系统中保证数据库与缓存数据一致性可从多个方面着手,以下是一些常见的解决方案: ### 最终一致性策略 多数场景可接受短暂不一致,可通过重试、定时任务(如每天全量同步)修复数据不一致问题。同时,设置合理的 TTL(生存时间),自动淘汰旧数据,还需监控缓存命中率、数据库与 Redis 的数据差异,及时发现不一致并处理[^2]。 ### 常见一致性方案 1. **先更新 DB,再删除缓存(Cache - Aside)**:这是最常用的方案,能实现最终一致,性能高且复杂度低。不过在并发、异常、网络延迟下,可能出现时序错乱,例如线程 A 更新数据库成功后,删除缓存失败(网络超时),此时线程 B 查询缓存会命中旧数据,导致数据库已更新,缓存仍是旧值的不一致情况[^3]。 2. **延迟双删(Delayed Double Delete)**:删除两次缓存,能防止并发问题,一致性较好,性能和复杂度处于中等水平[^3]。 3. **先删缓存,再更新 DB(Write - Behind)**:此方案风险高,不推荐使用,一致性差,性能高但复杂度低[^3]。 4. **基于 Binlog 的异步更新(如 Canal)**:异步监听数据库变更,实现最终一致,性能和复杂度都较高[^3]。 5. **分布式事务(如 Seata)**:能实现强一致,但性能差,复杂度高[^3]。 ### 针对不同并发情况的处理 对于并发几率很小的数据(如个人维度的订单数据、用户数据等),很少会发生缓存不一致,可以给缓存数据加上过期时间,每隔一段时间触发读的主动更新。就算并发很高,如果业务上能容忍短时间的缓存数据不一致(如商品名称,商品分类菜单等),给缓存加上过期时间依然可以解决大部分业务对于缓存的要求[^4]。 ### 推荐方案 推荐采用 Cache - Aside + 主动失效的方案,这是生产级常用方案[^3]。 以下是一个简单的 Java 示例代码,展示先更新 DB,再删除缓存的基本实现: ```java import java.util.HashMap; import java.util.Map; // 模拟数据库 class Database { private Map<String, String> data = new HashMap<>(); public void updateData(String key, String value) { data.put(key, value); } public String getData(String key) { return data.get(key); } } // 模拟缓存 class Cache { private Map<String, String> cacheData = new HashMap<>(); public void deleteCache(String key) { cacheData.remove(key); } public String getCache(String key) { return cacheData.get(key); } public void setCache(String key, String value) { cacheData.put(key, value); } } public class CacheDatabaseConsistency { private Database database = new Database(); private Cache cache = new Cache(); public void updateDataAndDeleteCache(String key, String value) { // 先更新数据库 database.updateData(key, value); // 再删除缓存 cache.deleteCache(key); } public String getData(String key) { String cacheValue = cache.getCache(key); if (cacheValue != null) { return cacheValue; } // 缓存未命中,从数据库获取 String dbValue = database.getData(key); if (dbValue != null) { // 将数据存入缓存 cache.setCache(key, dbValue); } return dbValue; } public static void main(String[] args) { CacheDatabaseConsistency consistency = new CacheDatabaseConsistency(); consistency.updateDataAndDeleteCache("product1", "New Product Name"); String data = consistency.getData("product1"); System.out.println(data); } } ```
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值