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

一、业务场景

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

(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. Python学习——列表

    定义:1.列表(list)是由一系列特定顺序排列的元素组成,可以包含字母,数字或者将任何东西加入列表. 2.列表的标识符号为 [ ],函数名称为list 3.列表是有序的 相关概念: 元素:列表中的值 ...

  2. spring+struts2+hibernate整合

    web.xml需要配置 <context-param> <param-name>contextConfigLocation</param-name> <par ...

  3. Files and Directories

    Files and Directories Introduction     In the previous chapter we coveredthe basic functions that pe ...

  4. 熊掌号:"搜索+信息流"双引擎与"百家号+熊掌号"双品牌内容平台

    一. 熊掌号是什么?熊掌号简单来说,就是"搜索 + 信息流"双引擎与"百家号 + 熊掌号"双品牌内容平台,上线了,对站长还是企业,都是一件好事.只要写出优质的原 ...

  5. iOS swift的xcworkspace多项目管理(架构思想)

    iOS  swift的xcworkspace多项目管理(架构思想) 技术说明: 今天在这里分享 swift下的 xcworkspace多项目管理(架构思想),能为我们在开发中带来哪些便捷?能为我们对整 ...

  6. 解决div里面img标签后面跟着空白符

    直接在div里面添加img标签有一个坑,img的高度会莫名添加几个像素的高度,查询之后才知道原来css2出现的问题. 引起这个问题原来是img标签默认基线引起的,vertical-align:base ...

  7. 晓莲说-何不原创:如何通过jad把class批量反编译成java文件

    背景:前几天在项目开发的时候遇到一个问题,那就是利用myeclipse编写好的一个项目打包成jar包后上传部署到服务器里,之后本地的项目被自己改来改去出现了一些问题,想着把上传到服务器里面的war包下 ...

  8. 翻译连载 | 附录 B: 谦虚的 Monad-《JavaScript轻量级函数式编程》 |《你不知道的JS》姊妹篇

    原文地址:Functional-Light-JS 原文作者:Kyle Simpson-<You-Dont-Know-JS>作者 关于译者:这是一个流淌着沪江血液的纯粹工程:认真,是 HTM ...

  9. 非一屏页面,出现遮罩层页面位置不动,并且遮罩层一屏显示。(pc,移动端都适用的方法)

    首先展示页面效果: 遮罩没出现的页面张酱紫:页面在楼层二这个位置. 遮罩显示:后面页面页面任停留在当前浏览位置,滚动条并未回顶部 下面来说说写法: css: 页面具体布局样式......(此处省略无数 ...

  10. deepin 常用设置

    1 不开特效和动画,开启透明无黑边 #!/bin/bash #开启 metacity 窗管合成,取代正在运行的窗管 deepin-metacity --composite --replace #关闭 ...