上一篇
双十一零点刚过,程序员小张突然收到客服部轰炸:"用户投诉支付成功后订单消失!" 排查发现——订单服务更新了MySQL,但Redis缓存还是旧数据,导致用户查询时看到"未支付"状态,这就是典型的Redis脏读现场!😱
当缓存(Redis)与数据库(MySQL等)数据不一致时,用户读取到过期或错误的数据,就像"脏衣服"混进了干净衣柜 👔 ➡️ 🧦
典型表现:
// 错误示范:先更DB再更Redis(中间可能崩溃) updateDB(data); // 步骤1 updateRedis(data); // 步骤2未执行
时间线 | 线程A(写) | 线程B(读) |
---|---|---|
t1 | 更新MySQL为X | |
t2 | 读取Redis旧值Y | |
t3 | 更新Redis为X |
MySQL事务提交后,Redis异步更新可能延迟(主从同步期间更明显)
def get_data(key): data = redis.get(key) if not data: data = db.query(key) # 回源查询 redis.setex(key, 300, data) # 设置过期时间 return data def update_data(key, value): db.update(key, value) # 先更新数据库 redis.delete(key) # 再删除缓存
注意:适合读多写少场景,可能短暂不一致但最终一致
// 写操作示例 public void updateOrder(Order order) { redis.del(order.id); // 第一次删除 db.update(order); // 更新数据库 delayQueue.add(order.id, 2s); // 延迟2秒后二次删除 }
优势:解决并发更新导致的脏数据残留
func UpdateWithLock(key string, value interface{}) { lock := redsync.New(mutex) if err := lock.Lock(); err == nil { defer lock.Unlock() db.Update(key, value) redis.Set(key, value) } }
代价:性能下降约20%,需权衡使用
SET user:123 '{ver: 5, name: "Alice"}' # 写入版本号
更新时校验版本号,防止旧数据覆盖新数据
location /api { redis_fail_timeout 100ms; # Redis超时自动降级查DB }
适用场景:保证可用性优先时
方案 | 一致性强度 | 实现复杂度 | 性能影响 | 适用场景 |
---|---|---|---|---|
Cache Aside | 最终一致 | 低 | 常规业务 | |
双删延迟队列 | 准实时 | 中 | 秒杀活动 | |
Binlog同步 | 强一致 | 高 | 支付/财务系统 | |
分布式锁 | 强一致 | 高 | 写密集小数据量 |
Redis脏读就像"薛定谔的缓存"——在你读取之前永远不知道数据是否最新 😅,根据业务场景选择合适方案:
没有银弹,只有权衡,现在就去检查你的缓存策略吧!🔍
本文由 闪章 于2025-08-02发表在【云服务器提供商】,文中图片由(闪章)上传,本平台仅提供信息存储服务;作者观点、意见不代表本站立场,如有侵权,请联系我们删除;若有图片侵权,请您准备原始证明材料和公证书后联系我方删除!
本文链接:https://vps.7tqx.com/wenda/514750.html
发表评论