巧用CAS解决数据一致性问题

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

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

一、业务场景

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

(1)从数据库查询用户现有余额 SELECT money FROM t_yue WHERE uid=$uid,不妨设查询出来的$old_money=100元

image.png

(2)业务层实施业务逻辑,比如购买一个80元的商品,并且打九折

if($old_money> 80*0.9) $new_money=$old_money-80*0.9=28

image.png

(3)将数据库中的余额进行修改 UPDAtE t_yue SET money=$new_money WHERE uid=$uid

image.png

在并发量低的情况下,这个流程没有任何问题,原有金额100元,购买了80元的九折商品(72元),剩余28元。

二、潜在的问题

在分布式环境中,如果并发量很大,这种“查询+修改”的业务很容易出现数据不一致。极限情况下,可能出现这样的异常流程:

(1)业务1和业务2同时查询余额,是100元
image.png

(2)业务1和业务2进行逻辑计算,算出各自业务的余额,假设业务1算出的余额是28元,业务2算出的余额是38元

image.png

(3)业务1对数据库中的余额先进行修改,设置成28元。

业务2对数据库中的余额后进行修改,设置成38元。

image.png

此时异常出现了,原有金额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的时候,加上初始条件的比对。

===【完】===

image.png

目录
相关文章
|
网络协议 Ubuntu
如何修改wsl的Ubuntu的dns,固定dns
本文介绍了如何在 WSL 中配置网络设置。首先,修改 `/etc/wsl.conf` 文件,添加 `generateResolvConf = false`。其次,编辑 `/etc/resolv.conf` 文件,指定 DNS 服务器地址。最后,重启 WSL 即可生效。
2513 2
|
3月前
|
Rust 安全 Docker
使用 uv 一键创建并激活 Python 虚拟环境(附完整脚本)
本文介绍基于 `uv` 的自动化脚本 `activate_env.sh`,一键完成安装 uv、创建并激活虚拟环境、安装依赖及环境信息输出,提升 Python 项目初始化效率,适用于个人开发、团队协作与 CI/CD 场景。
|
监控 关系型数据库 数据库
PostgreSQL 恢复模式错误日志增强 - 提供正在恢复的WAL(XLOG)文件位置
标签 PostgreSQL , the database system is starting up , the database system is in recovery mode 背景 当数据库异常停库,再次启动时。
3563 0
EMQ
|
存储 JSON 数据库
MQTTX 1.10.0 发布:CLI高级文件管理与配置
在本次更新中,CLI 版本在文件管理和配置功能方面进行了显著增强。主要更新包括:支持从文件中读取和写入消息、高级配置选项、文本输出模式、以及改进的日志记录。此外,桌面版本现在支持数据库重建,以防止文件损坏引起的问题,并且能更好地处理大数据的展示。这些更新希望为所有 MQTTX 用户提供更加强大和用户友好的体验。
EMQ
1134 255
MQTTX 1.10.0 发布:CLI高级文件管理与配置
|
5月前
|
人工智能 机器人 定位技术
隔壁火锅店天天排队,老板悄悄做了这件事?
成都火锅店两周营业额提升38%?秘诀是GEO优化!AI搜索时代,让店铺被推荐成“附近首选”。从信息标记到场景内容布局,三步打造AI推荐门店。别再等顾客找你,让AI把客流送到门口。
395 0
|
缓存 前端开发 安全
网站显示不安全的解决办法
当浏览器提示网站“不安全”时,通常是HTTPS配置或证书问题。解决方法包括:检查网址、验证证书状态(是否失效、域名匹配、CA受信任),确保证书链完整,避免自签名证书;解决混合内容问题,确保所有资源使用HTTPS;修正服务器配置,强制HTTP跳转HTTPS,启用TLS 1.2/1.3,添加HSTS;检查本地时间与浏览器缓存。按此步骤排查,可有效解决问题。
|
机器学习/深度学习 数据可视化 搜索推荐
使用Python实现深度学习模型:智能睡眠监测与分析
使用Python实现深度学习模型:智能睡眠监测与分析
1741 2
|
网络协议 网络架构 Windows
IPv6中的四类地址
【4月更文挑战第22天】IPv6地址分为3类:单播、任播和多播。IPv6中还有特殊的未指定地址和环回地址
|
缓存 关系型数据库 MySQL
mysql报Host is blocked because of many connection errors; unblock with ‘mysqladmin flush-hosts‘。
mysql报Host is blocked because of many connection errors; unblock with ‘mysqladmin flush-hosts‘。
1305 0
|
Linux Windows Ubuntu
grub2与grub区别
<ul style="margin: 0px; padding: 0px; border: 0px; list-style: none; color: rgb(50, 62, 50); font-family: simsun; font-size: 14px; line-height: 21px;"> <li style="margin: 0px 0px 0px 30px; padding:
6304 0

热门文章

最新文章