当前位置: 首页 > news >正文

电商缓存强一致方案:数据库锁保障

背景

在电商系统中,为提升商品详情页的访问速度,我们通常会使用 Redis 缓存商品信息。获取商品信息时,会先从 Redis 中查询,若未命中,则从数据库获取并写入 Redis 后返回。

但后台存在商品信息修改的操作,此时如何确保 Redis 缓存与数据库数据一致?即如何保证商品详情接口返回的数据与数据库中的数据完全一致?

方案 1:常规缓存更新策略及问题分析

商品表结构(t_goods)

字段

类型

说明

goodsId

int

商品 id

stock

int

库存

核心逻辑

获取商品详情接口

step1:从Redis中查询商品信息,若存在则直接返回;若不存在,继续下一步

step2:从数据库中查询商品信息

step3:将查询到的商品信息写入Redis

step4:返回商品信息

后台更新商品逻辑

step1:更新商品信息到数据库

step2:删除Redis中对应的商品记录

预期目标

在并发情况下,数据库与 Redis 中的数据保持一致。例如,若数据库中商品库存为 10,那么商品详情接口返回的库存也应为 10。

并发场景验证

假设商品 1 的初始库存为 10,模拟 3 个线程同时操作:

  • thread1:执行商品更新操作(将库存改为 0)
  • thread2、thread3:调用商品详情接口

时间点

thread1(更新商品)

thread2(获取商品信息)

thread3(获取商品信息)

T1

step1:Redis 中无商品信息

T2

step2:从数据库查询到商品信息(goodsId:1,stock:10)

T3

step1:将数据库中商品 1 的库存更新为 0

T4

step2:删除 Redis 中商品 1 的记录

T5

step3:将库存 10 的商品信息写入 Redis

T6

step4:返回商品信息(stock:10)

T7

step1:从 Redis 中获取到库存 10 的商品信息

T8

step2:返回商品信息(stock:10)

结果分析

此时数据库中商品 1 的库存为 0,而 Redis 中为 10,数据不一致,该方案无法满足一致性要求。

方案 2:基于数据库锁的强一致性方案

问题根源

方案 1 中数据不一致的核心原因是:商品更新操作与商品查询操作并行执行时,查询线程可能在更新线程删除缓存后,仍将旧数据写入 Redis,导致缓存与数据库数据不符。

要解决此问题,需让更新操作与查询操作串行执行,确保查询操作能获取到最新的数据库数据。

优化方案

通过数据库的for update行锁,实现更新操作与查询操作的互斥,保证数据一致性。

后台更新商品逻辑(优化后)

step1:开启数据库事务

step2:更新商品信息到数据库

step3:执行加锁查询:select * from t_goods where goodsId = #{goodsId} for update;(锁定该商品记录)

step4:删除Redis中对应的商品记录

step5:提交数据库事务(释放锁)

获取商品详情接口(优化后)

step1:从Redis中查询商品信息,若存在则直接返回;若不存在,继续下一步

step2:开启数据库事务

step3:执行加锁查询:select * from t_goods where goodsId = #{goodsId} for update;(等待更新操作释放锁)

step4:将查询到的最新商品信息写入Redis

step5:提交事务(释放锁)

step6:返回商品信息

并发场景验证

同样模拟商品 1 库存从 10 更新为 0 的场景,3 个线程操作如下:

时间点

thread1(更新商品)

thread2(获取商品信息)

thread3(获取商品信息)

T1

step1:Redis 中无商品信息

T2

step1:开启事务,将数据库中商品 1 的库存更新为 0

T3

step2:执行select ... for update,锁定商品 1 记录

T4

step3:删除 Redis 中商品 1 的记录

T5

step2:开启事务,执行select ... for update,等待锁释放

T6

step4:提交事务,释放锁

T7

step3:获取到数据库中最新商品信息(stock:0)

step1:Redis 中无商品信息

T8

step4:将库存 0 的商品信息写入 Redis

T9

step5:提交事务,释放锁

step2:开启事务,执行select ... for update

T10

step6:返回商品信息(stock:0)

step3:获取到数据库中库存 0 的商品信息

T11

step4:将库存 0 的商品信息写入 Redis

T12

step5:提交事务,释放锁

T13

step6:返回商品信息(stock:0)

结果分析

数据库与 Redis 中商品 1 的库存均为 0,数据一致,该方案实现了强一致性。

方案解析

核心原理

利用数据库的for update行锁,使商品更新操作与查询操作串行执行:

  • 当更新操作执行select ... for update时,会锁定该商品记录,其他查询操作执行相同 SQL 时需等待锁释放。
  • 待更新事务提交后,查询操作才能获取到最新数据并写入 Redis,确保缓存与数据库数据一致。

优势

  • 强一致性:通过锁机制严格保证缓存与数据库数据一致。
  • 实现简单:无需引入额外中间件,依托数据库自身锁机制即可实现。
  • 适用性广:适用于对数据一致性要求高的场景,如商品库存、价格等核心信息。

注意事项

  • 加锁会增加系统开销,可能降低并发性能,需根据业务场景权衡。
  • 事务要尽可能短,减少锁持有时间,降低阻塞影响。

总结

在电商系统商品信息缓存场景中,若需保证 Redis 与数据库数据强一致,推荐采用方案 2:

  1. 更新商品时,通过for update锁定记录,确保更新期间查询操作等待。
  1. 查询商品时,同样通过for update获取最新数据,避免写入旧数据到缓存。

该方案将并发操作转换为顺序执行,从根源上解决了数据不一致问题,虽可能降低部分并发性能,但能保障核心数据的准确性,适合对一致性要求高的业务场景。在实际应用中,可根据业务对一致性和性能的要求,灵活选择合适的方案。


文章转载自:
http://bombay.isnyv.cn
http://aflutter.isnyv.cn
http://changeabout.isnyv.cn
http://choreiform.isnyv.cn
http://annie.isnyv.cn
http://animatedly.isnyv.cn
http://barkeeper.isnyv.cn
http://ceskoslovensko.isnyv.cn
http://australasia.isnyv.cn
http://ceramic.isnyv.cn
http://animism.isnyv.cn
http://catenate.isnyv.cn
http://cemically.isnyv.cn
http://bookstack.isnyv.cn
http://antinoise.isnyv.cn
http://bolter.isnyv.cn
http://anthropological.isnyv.cn
http://battlesome.isnyv.cn
http://asphyxiate.isnyv.cn
http://baryonic.isnyv.cn
http://archdukedom.isnyv.cn
http://archdiocese.isnyv.cn
http://admirably.isnyv.cn
http://chronometric.isnyv.cn
http://canescent.isnyv.cn
http://chloroacetic.isnyv.cn
http://brainwork.isnyv.cn
http://benguela.isnyv.cn
http://babysitter.isnyv.cn
http://carabinier.isnyv.cn
http://www.dtcms.com/a/280647.html

相关文章:

  • 设计模式开篇:设计模式的七大核心原则
  • kube-proxy 中 IPVS 与 iptables
  • PyTorch笔记7----------计算机视觉基础
  • OpenCV 伽马校正函数gammaCorrection()
  • MODIS_Landsat_Sentinel2星源分幅简述【20250715】
  • 视频编码中熵编码之基于上下文的变长编码(Huffman霍夫曼编码和指数哥伦布)
  • 【YOLOv11-目标检测】06-模型部署(C++)
  • 06_pt-table-sync 工具解决 MySQL 主从数据不一致
  • conda环境保存(后期再来整理)
  • etcd自动压缩清理
  • 2-Nodejs运行JS代码
  • iOS高级开发工程师面试——Swift
  • Fiddler 中文版抓包实战 构建标准化调试流程提升团队协作效率
  • echarts 绘制3D中国地图
  • React强大且灵活hooks库——ahooks入门实践之开发调试类hook(dev)详解
  • PostgreSQL 数据库中 ETL 操作的实战技巧
  • React源码6 三大核心模块之一:commit, finishConcurrentRender函数
  • 前端学习笔记:React.js中state和props的区别和联系
  • haproxy负载均衡
  • AntV G6 基础元素详解(React版)
  • 【PTA数据结构 | C语言版】创建哈夫曼树
  • 使用ZYNQ芯片和LVGL框架实现用户高刷新UI设计系列教程(第二十一讲)
  • 【PDF识别改名】使用京东云OCR完成PDF图片识别改名,根据PDF图片内容批量改名详细步骤和解决方案
  • 同样是“跳转”,为何forward地址栏不变,redirect会变?
  • RNN、GRU 与 LSTM 计算成本深入对比
  • 基于光场相机的激光增材制造熔池温度场原位多眼监测​​
  • 【zynq7020】PL的“Hello LED”
  • FPGA高端图像ISP培训课程,提供工程源码+视频教程+FPGA开发板
  • Softhub软件下载站实战开发(十八):软件分类展示
  • 使用LNMP一键安装包安装PHP、Nginx、Redis、Swoole、OPcache