巧用CAS解决数据一致性问题
缘起:在高并发的分布式环境下,对于数据的查询与修改容易引发一致性问题,本文将分享一种非常简单但有效的优化方法。
一、业务场景
业务场景为,购买商品的过程要对余额进行查询与修改,大致的业务流程如下:
(1)从数据库查询用户现有余额 SELECT money FROM t_yue WHERE uid=$uid,不妨设查询出来的$old_money=100元
(2)业务层实施业务逻辑,比如购买一个80元的商品,并且打九折
if($old_money> 80*0.9) $new_money=$old_money-80*0.9=28
(3)将数据库中的余额进行修改 UPDAtE t_yue SET money=$new_money WHERE uid=$uid
在并发量低的情况下,这个流程没有任何问题,原有金额100元,购买了80元的九折商品(72元),剩余28元。
二、潜在的问题
在分布式环境中,如果并发量很大,这种“查询+修改”的业务很容易出现数据不一致。极限情况下,可能出现这样的异常流程:
(1)业务1和业务2同时查询余额,是100元
(2)业务1和业务2进行逻辑计算,算出各自业务的余额,假设业务1算出的余额是28元,业务2算出的余额是38元
(3)业务1对数据库中的余额先进行修改,设置成28元。
业务2对数据库中的余额后进行修改,设置成38元。
此时异常出现了,原有金额100元,业务1扣除了72元,业务2扣除了62元,最后剩余38元。
三、问题原因
高并发环境下,对同一个数据的并发读(两边都读出余额是100)与并发写(一个写回28,一个写回38)导致的数据一致性问题。
四、原因分析
业务1的写回:原有金额100,这是一个初始状态,写回金额28,理论上只有在原有金额为100的时候才允许写回成功,这一步没问题。
业务2的写回:的原有金额100,这是一个初始状态,写回金额38,理论上只有在原有金额为100的时候才允许写回成功,可实际上,这个时候数据库中的金额已经变为28了,这一步的写操作不应该成功。
五、简易解决方案
在set写回的时候,加上初始状态的条件compare,只有初始状态不变时,才允许set写回成功,这正是大家常说的“Compare And Set”(CAS),是一种常见的降低读写锁冲突,保证数据一致性的方法。
六、业务的升级
业务线使用CAS解决高并发时数据一致性问题,只需要在进行set操作时,compare一下初始值,如果初始值变换,不允许set成功。
对于上文中的业务场景,只需要将“UPDAtEt_yue SET money=$new_money WHERE uid=$uid”升级为
“UPDAtE t_yue SETmoney=$new_money WHERE uid=$uid AND money=$old_money”即可。
并发操作发生时:
业务1执行 => UPDAtE t_yue SET money=28 WHERE uid=$uid AND money=100
业务2执行 => UPDAtE t_yue SET money=38 WHERE uid=$uid AND money=100
【这两个操作同时进行时,只能有一个执行成功】。
七、怎么判断哪个执行成功,哪个执行失败
set操作,其实无所谓成功或者失败,业务能通过affect rows得知哪个修改没有成功:
执行成功的业务,affect rows为1
执行失败的业务,affect rows为0
八、总结
高并发“查询并修改”的场景,可以用CAS(Compare and Set)的方式解决数据一致性问题。对应到业务,即在set的时候,加上初始条件的比对。
以上内容均来自微信公众号“架构师之路”胡剑老师的文章,欢迎关注。
巧用CAS解决数据一致性问题的更多相关文章
- 每日学习心得:Linq解决DataTable按照某一列的值排序问题/DataTable 导出CSV文件/巧用text-overflow解决数据绑定列数据展示过长问题
2013-8-5 1 Linq解决DataTable按照某一列的值排序 在之前的总结中提到过对拼接而成的复合的DataTable按照某一列值的大小排序,那个主要的思想是在新建表结构时将要排序的那一列的 ...
- 巧用 Lazy 解决.NET Core中的循环依赖关系
原文作者: Thomas Levesque 原文链接:https://thomaslevesque.com/2020/03/18/lazily-resolving-services-to-fix-ci ...
- 巧用setTimeout解决阻塞导致页面无法重绘
项目中遇到一个javascript问题,大致如下: <!doctype HTML> <html> <head> <meta charset="utf ...
- 巧用FileShare解决C#读写文件时文件正由另一进程使用的bug
在使用C#进行文件读写的时候,一旦对文件操作频繁,总会碰到一些令人措手不及的意外.例如经常会碰到的一个问题: System.IO.IOException: 文件“XXX”正由另一进程使用,因此该进程无 ...
- 巧用map解决nginx的Location里if失效问题
需求: Nginx根据参数来输出不同的header 我们想用Nginx来判断一些通用的参数, 根据参数情况在输出中不同的header, 或者cookie, 那么根据正常思路, 有如下配置: locat ...
- 巧用 Base62 解决字段太短的问题
最近银联一纸 259 号改造通知,所有支付机构开始改造支付交易,上传终端信息. 不知道其他支付机构的小伙伴针对这次改造是否开始了? 由于这次银联给的时间非常少,我们这边改动涉及到相关上游一起改造,所以 ...
- MT【110】巧设法向量解决距离问题
分析:设面的法向量$n=(x,y,z)$
- MySQL实战积累
IFNULL(expr1,expr2)的用法:假如expr1不为NULL,则IFNULL()的返回值为 expr1; 否则其返回值为expr2. 索引:http://www.cnblogs.com ...
- RAFT实践
1. raft org 2. paper 3. animation 4. python examples Raft-python 这个不是很好用. simpleRaft Raft算法分析与理解 ...
随机推荐
- rem布局配合less的快速开发
最近在进行静态页面的制作,为了方便和快速的布局,自己整理了一套工具可以快速的进行工作,剩余的时间大家都懂的,话不多说,来看具体的东西吧! 1.ps 下载这个软件→cutterman 十分强大的切图功能 ...
- C#实现DirectShow技术开发准备
DirectShow组件在“C:\WINDOWS\system32”目录下的Quartz.dll动态库中,要使C#代码引用COM对象和接口,必须将COM类型库转换为.NET框架元数据,从而有效地创建一 ...
- 盒子端 CSS 动画性能提升研究
不同于传统的 PC Web 或者是移动 WEB,在腾讯视频客厅盒子端,接大屏显示器(电视)下,许多能流畅运行于 PC 端.移动端的 Web 动画,受限于硬件水平,在盒子端的表现的往往不尽如人意. 基于 ...
- [转载] Redis系统性介绍
转载自http://blog.nosqlfan.com/html/3139.html?ref=rediszt 虽然Redis已经很火了,相信还是有很多同学对Redis只是有所听闻或者了解并不全面,下面 ...
- SSD中的GC机制以及Trim
GC(Garbagecollection)垃圾回收 所谓GC就是把一个闪存块里的"有效"页数据复制到一个"空白"块里,然后把这个块完全擦除.GC是 SSD里的 ...
- 【转】MYSQL DBA知识了解-面试准备
http://www.itpub.net/forum.php?mod=viewthread&tid=1825849 公司招聘MySQL DBA,也面试了10个2年MySQL DBA工作经验的朋 ...
- red5 自定义文件存放目录
Red5 流媒体服务器 自定义文件存放目录 Red5在正常情况下,安装之后文件必须存放在Red5安装目录下的oflaDemo\streams中,不能自定义存放目录,例如Red5 安装在C盘,但是我的文 ...
- HTTPS 传输优化详解之动态 TLS Record Size
笔者在过去分析了诸多可以减少 HTTPS 传输延迟的方法,如分布式 Session 的复用: 启用 HSTS,客户端默认开启 HTTPS 跳转:采用 HTTP/2 传输协议:使用 ChaCha20-P ...
- java中处理json各种各样转换方法
JSON 即 JavaScript Object Natation,它是一种轻量级的数据交换格式,非常适合于服务器与 JavaScript 的交互.本文将快速讲解 JSON 格式,并通过代码示例演示如 ...
- 开始你的第一个npm脚本工具
在实际开发中,一般刚开始一个项目或者刚接手一个项目,我们会运行 npm install 下载安装所有依赖, 在实际开发中,可能也会使用各种命令行-- 来提高我们开发的效率. 与它相处了这么久,你真的了 ...