余额操作在大多数系统都是不可缺少和不允许出现问题的 如何修改余额 , 这个问题可能在实际项目中 没那么简单;
如何修改余额
假设一个用户数据 :
id⇒12 | user_name⇒ 放放 | fee⇒ 30 | updated_at ⇒ 2019-09-06 15:51:33
修改余额
1 | //消费金额 |
按照正常逻辑来说上面更新余额是没问题的
但是如果发生并发 当 A 跟 B 同时发起请求时
请求 A 查询出余额为 30
请求 B 查询出余额为 30
请求 B 更新余额为 20
请求 A 更新余额为 20
最终用户余额为 20
也就是说 用户余额为 30 两个请求都消费了 10 元 即 30 - 10 -10 =10 但是当发生并发请求时 余额最终为 20 这里的金额是错误的
在请求 B 修改了 余额之后 请求 A 的查询出来的余额已经不是正确的了 导致了 余额更新错误
常见的解决办法 添加数据库的行锁
当请求 A 执行时 先加入锁 阻塞 请求 B 直到 请求 A 完成之后 请求 B 才继续执行
当然使用事务 并不是那么明智
1 | //开始事务 |
这里如果发生同时修改产生并发 将只有一边修改成功 这时候如果产生失败 可以对他进行重试等操作
为什么不使用 减等于 的 sql 语句
例如 :
update users set fee = fee - $spend where id = 12 ;
这里要再加上余额的判断避免出现 负数金额
update users set fee = fee - $spend where id = 12 and fee >= $spend ;
稍微改一下这里的更新 语句 也能完成正确的更新 就算是并发也都将正常
但是这样做将产生一个问题 不幂等
什么是不幂等 ?
在相同的条件下 , 执行同一请求,得到的结果相同才符合幂等性
也即是说
fee = fee - $spend 不幂等
fee = $newFee 幂等
不幂等的情况下 如果发生重复执行的情况将产生重复扣款
事实上实际业务如何正确的扣款 根据业务的实际情况 可能需要注意更多细节 , 越大的业务量,需要面对更多的问题处理更多的细节. 以上的方案也仅仅是最基础的处理 实际情况需要更多的耐心和思考 共勉之 ~
以上.