缘起:在高并发的分布式环境下,对于数据的查询与修改容易引发一致性问题,本文将分享一种非常简单但有效的优化方法。

一、业务场景

业务场景为,购买商品的过程要对余额进行查询与修改,大致的业务流程如下:

(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解决数据一致性问题的更多相关文章

  1. 每日学习心得:Linq解决DataTable按照某一列的值排序问题/DataTable 导出CSV文件/巧用text-overflow解决数据绑定列数据展示过长问题

    2013-8-5 1 Linq解决DataTable按照某一列的值排序 在之前的总结中提到过对拼接而成的复合的DataTable按照某一列值的大小排序,那个主要的思想是在新建表结构时将要排序的那一列的 ...

  2. 巧用 Lazy 解决.NET Core中的循环依赖关系

    原文作者: Thomas Levesque 原文链接:https://thomaslevesque.com/2020/03/18/lazily-resolving-services-to-fix-ci ...

  3. 巧用setTimeout解决阻塞导致页面无法重绘

    项目中遇到一个javascript问题,大致如下: <!doctype HTML> <html> <head> <meta charset="utf ...

  4. 巧用FileShare解决C#读写文件时文件正由另一进程使用的bug

    在使用C#进行文件读写的时候,一旦对文件操作频繁,总会碰到一些令人措手不及的意外.例如经常会碰到的一个问题: System.IO.IOException: 文件“XXX”正由另一进程使用,因此该进程无 ...

  5. 巧用map解决nginx的Location里if失效问题

    需求: Nginx根据参数来输出不同的header 我们想用Nginx来判断一些通用的参数, 根据参数情况在输出中不同的header, 或者cookie, 那么根据正常思路, 有如下配置: locat ...

  6. 巧用 Base62 解决字段太短的问题

    最近银联一纸 259 号改造通知,所有支付机构开始改造支付交易,上传终端信息. 不知道其他支付机构的小伙伴针对这次改造是否开始了? 由于这次银联给的时间非常少,我们这边改动涉及到相关上游一起改造,所以 ...

  7. MT【110】巧设法向量解决距离问题

    分析:设面的法向量$n=(x,y,z)$

  8. MySQL实战积累

    IFNULL(expr1,expr2)的用法:假如expr1不为NULL,则IFNULL()的返回值为   expr1; 否则其返回值为expr2. 索引:http://www.cnblogs.com ...

  9. RAFT实践

    1.  raft org 2. paper 3. animation 4. python examples Raft-python  这个不是很好用. simpleRaft Raft算法分析与理解  ...

随机推荐

  1. MSSQL 常用操作

    0.GUID去除横线和变换为小写 SELECT LOWER(REPLACE(LTRIM(NEWID()),'-','')) 1.IDENTITY 函数说明 IDENTITY ( data_type [ ...

  2. [转]如何查询SQL Server连接数

    1.获取SQL Server允许同时用户连接的最大数 SELECT @@MAX_CONNECTIONS 2.获取当前指定数据库的连接信息 SELECT * FROM master.dbo.syspro ...

  3. maven学习之二

    三 profile介绍 可以有多个地方定义profile.定义的地方不同,它的作用范围也不同. (1)    针对于特定项目的profile配置我们可以定义在该项目的pom.xml中. (2)     ...

  4. 分享一篇vue项目规范

    最近 Vue 用的比较多,而且因为公司里有实习生,当几个人写一个项目的时候,会出现很多问题,最麻烦的就是规范不统一,之前我有一篇文章是说, vue 是比较有规范的一种框架了,但是也会出现很多问题,所以 ...

  5. 件测试博客日记Day03-11.17日 —— 赵天宇 —— 禅道的使用和配置详细版

    说在维基百科先查找关于禅道相关知识,发现有关于禅道这个项目管理软件的详细介绍,然后将相关的介绍进行整理写入文档,在禅道的介绍中也有下载地址并进行安装. (1)软件的基本情况 a.中文名称:禅道项目管理 ...

  6. jq获取浏览器的高度

    // console.log("2-"+$(window).height()); //浏览器当前窗口可视区域高度 // console.log("3-"+$(d ...

  7. Unity与iOS原生代码之间的相互调用

    1.Unity调用iOS: 1.1.在Unity C#中: [ DllImport( "__Internal" )] private static extern int _show ...

  8. 学习MySQL(上)

    具体实例 1.PHP 服务器组件 对于初学者建议使用集成的服务器组件,它已经包含了 PHP.Apache.Mysql 等服务,免去了开发人员将时间花费在繁琐的配置环境过程. Window 系统可以使用 ...

  9. 利用python对excel进行数据剔除

    需求分析: 判断excel2表中的某个唯一字段是否满足条件,如果满足条件,就在excel1中进行查询,若存在excel中,就将该数据进行剔除. python脚本的实现: from __future__ ...

  10. 中文编程语言之Z语言初尝试: ZLOGO 4

    原文: https://zhuanlan.zhihu.com/p/31505895. 作者为本人. @TKT2016 开发的Z语言(ZLOGO是它的一个部分)是本人至今看到的唯一一个仍活跃开发的开源且 ...