高并发下如何保证数据的一致性

拿转账来说,在高并发下场景下,对账户余额操作的一致性,是非常重要的。如果代码写的时候没考虑并发一致性,就会导致公司亏损。所以本篇主要聊一下,如何在并发场景下,保证账户余额的一致性。

扣款流程

伪代码

public void transfer(Long id, Double payAmount){
   # 查询账户总额
   SELECT amount FROM account WHERE id=${id};

   #更新余额  
   UPDATE account SET amount=amount-payAmount WHERE id=${id};
}

以上流程如果并发量非常低的情况下是没问题的,但是如果在高并发下是很容易出现问题的。

在高并发下会出现什么问题?

假设 订单A 和 订单B 在同一时间都查询到了,账户余额为1000

订单A 扣款200,订单B 扣款 100,都满足1000-减去扣款金额大于0

执行扣款,订单A 修改账户余额为800,订单B 修改为账户余额为900

此时就出现问题了,如果 订单A 先执行更新,订单B 后执行,那么账户余额最终为900,反之为 800,都不正确,正确余额应该是700,那怎么处理呢?

并发扣款怎么处理?

使用悲观锁

在执行扣款时使用分布式锁或者数据库的 for update 对账户数据进行行级锁,使执行并发操作串型化操作。在这里使用 for update 做操作,这是数据库最简单的,但不推荐。

使用 for update 的方式,可能会带来很多问题,因为他是一个行级锁,高并发的情况下可能会导致死锁、客户端连接超时等问题。

如果一定要使用 for update,要注意 where 条件是唯一索引,否则会导致多行数据被锁,同时必须要开始事务,否则 for update 没效果,使用分布式数据库中间件还要注意,for update 可能会路由到读节点上。

使用乐观锁(CAS)

乐观锁的方式也就是是CAS的方式,适合并发量不高情况,如果并发量高大概率都失败在重试,开销也不比悲观锁小。

增加版本号方式

在查询余额时,加上版本号。

SELECT amount,version FROM account WHERE id=${id}

每次更新余额时,必须版本号相等,并且版本号每次要修改。

UPDATE account SET amount=余额,version=newVersion WHERE id=${id} AND version=${oldVersion}

使用原有金额值比对更新

在执行账户余额更新时,where 条件中增加第一次查出来的账户余额,即初始余额,如果在执行更新时,初始余额没变则更新成功,否则肯定是更新了,同时数据库也会返回受影响的行数,来判断是否更新成功,如果没成功就再次重试,同时还要考虑幂等性。

UPDATE account SET amount=余额 WHERE id=${id} AND amount=${oldAmount}

订单A 执行

UPDATE account SET amount=800 WHERE id=${id} AND amount=1000;

订单B 执行

UPDATE account SET amount=900 WHERE id=${id} AND amount=1000;

以上两笔执行只有一笔能成功,因为 amount 变了。

注意

在使用乐观锁时需要注意 ABA 的问题。拿上述场景举例:

订单A:获取出账户余额为 1000,期望余额是 1000 的时候,才能修改成功。

订单B:取了 100,将余额修改成了 900。

订单C:存进去了100,将余额修改成了 1000。

订单A:检查账户余额为 1000,进行扣款 200,账户余额变成了 800。

以上场景账户资金损失吗?没有,不过为了避免产生误解,推荐还是使用版本号的方式!

总结

在并发量高的情况下推荐使用悲观锁的方式,如果并发量不高可以考虑使用乐观锁,但是要注意幂等性,推荐使用版本号方式。同时乐观锁场景要注意 aba 的问题。

  • 22
    点赞
  • 27
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
保证高并发时数据一致性是一个复杂的问题,以下是一些常用的策略和技术: 1. 事务管理:使用数据库事务来保证一组操作的原子性,要么全部成功,要么全部回滚。通过在关键操作上使用事务,可以确保在并发场景下数据的一致性。 2. 锁机制:使用锁来实现对共享资源的互斥访问,防止多个线程同时修改同一份数据。可以使用悲观锁或乐观锁来保证数据一致性。 3. 并发控制:使用并发控制算法来解决并发访问数据时可能出现的冲突问题,如读写锁、信号量、版本控制等。这些机制可以确保在高并发情况下数据的一致性和正确性。 4. 分布式事务:在分布式系统中,可以使用分布式事务协调器(如XA协议)来管理多个参与者之间的事务,保证数据在不同节点之间的一致性。 5. 缓存策略:合理使用缓存来减轻数据库负载,但需要注意及时更新缓存,以避免缓存与数据库数据不一致的情况发生。 6. 数据复制与同步:通过数据复制和同步机制将数据在多个节点之间进行同步,确保数据的一致性。常见的方法有主从复制、集群复制等。 7. 一致性哈希算法:在分布式环境中,使用一致性哈希算法可以有效地解决节点的动态加入和删除带来的数据迁移问题,保证数据一致性。 需要根据具体场景和需求选择合适的策略和技术来保证高并发时数据的一致性
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

旷野历程

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值