加锁失效,非锁之过,加之错也

京东云开发者
• 阅读 57

作者:京东零售 邢成

引言

多个进程或线程同时(或着说在同一段时间内)访问同一资源会产生并发问题。



银行两操作员同时操作同一账户就是典型的例子。比如A、B操作员同时读取一余额为1000元的账户,A操作员为该账户增加100元,B操作员同时为该账户减去 50元,A先提交,B后提交。 最后实际账户余额为1000-50=950元,但本该为1000+100-50=1050。这就是典型的并发问题



从事零售供应链库存业务,对库存数量操作增减十分频繁,同样存在类似上述银行取款遇到的问题,库存数量操作有误势必给前台销售产生损失影响,因此需要关注对库存数量并发操作下的一致性。



下面通过一个真实的案例分享在并发情况下如何保证库存数量的准确性。

问题是什么-加锁失效

看看下面这段流程和代码,思考会有并发问题吗? 加锁失效,非锁之过,加之错也

1. 加锁前 ,获取箱子明细数据,此处在锁之外,存在并发脏读问题

加锁失效,非锁之过,加之错也



2. 加锁后 ,并进行箱子上架分批次回传业务处理

加锁失效,非锁之过,加之错也

3. 加锁后, 更新箱子明细上架数量逻辑:已上架数量 = 加锁前的明细数据(脏读) + 报文回传的明细数据 直接进行行更新

加锁失效,非锁之过,加之错也

原因是什么-加锁的位置不正确

加锁失效,非锁之过,加之错也

核心的问题原因

1.业务分布式锁失效: 使用分布式锁加锁了,但是仍然使用加锁前查询的数据,导致出现脏读

2.Mysql锁失效: 数据库更新时,未上任何锁,导致脏读的数据直接覆盖更新当前行



有同学这时问了,为啥防重码也没有生效呢?

防重码主要是用作幂等逻辑的,同一个请求多次处理,结果仍然是相同的。

但是这是两次不同的请求,防重码是不同的,因此不能只依赖防重码保证一致性。



解决方案有哪些

1、代码层面: 使用锁(如互斥锁、读写锁、分布式锁等)来控制资源的访问,数据获取的全部操作都需要再获取锁后才进行。

将获取箱子明细的代码移动到加锁之后,只有获取到分布式锁,才能执行分批次上架查询和更新(串行化)

加锁失效,非锁之过,加之错也



对应改造后的代码:

加锁失效,非锁之过,加之错也



2、数据库层面: 实现事务管理,确保数据的一致性;合理设置事务隔离级别,以防止脏读、或者采用乐观锁或悲观锁来处理并发更新,合理设计查询效率,减少锁竞争。

数据库的并发上锁处理和业务代码的上锁是互补的关系

因为无法保证后续业务的调整或其他业务代码的调用能始终保持获取数据的一致性,数据库的并发上锁处理更多是一种兜底保证机制。



乐观锁更新

加锁失效,非锁之过,加之错也





悲观锁更新



加锁失效,非锁之过,加之错也





扩展方案

1.应用程序设计: 在应用程序设计阶段,尽量避免长时间持有数据库连接或事务,减少并发操作的可能性,利用AI代码评审或者人工提前找出可能出现并发问题的地方;合理设置锁的粒度,避免锁失效。



  1. 网络负载层面: 采用限流控制访问频率;采用分布式数据库,进行数据分片,降低单节点并发压力;使用负载均衡,将网络请求分发到不同的服务器,提高系统处理并发的能力,防止系统过载。



1.请求层面: 前端点击防重、系统幂等防重、尽可能降低同一请求的多次重试访问引起的一致性问题。



通过以上措施,可以在不同层面有效地防止并发问题,保证系统的数据的一致性

点赞
收藏
评论区
推荐文章
分布式事务解决方案
一、什么是分布式事务在早期的单体架构时期,所有的数据操作都在同一个数据库里面进行,比如:A给B转100块钱,A的账户余额100,B的账户余额100,这两个操作放在同一个事务里面即可,由数据库来保证事务的原子性、一致性、持久性、隔离性。但是
AWS国庆双重礼,仅限7天
自2021年10月1日00:00起至2021年10月7日24:00,新注册并激活(需全部完成账号注册的五个步骤,否则账号状态并未激活)AWS海外区域账户,填写页面下方表单,即可申领价值$200美元的AWS海外区域账户服务抵扣券直充到您的账户,用以抵扣服务消费,助您轻松体验多个云迁移应用场景。同时,您还可获赠AWS精美祥云纪念T恤一件。,仅限7天$20
AWS国庆双重礼,仅限7天
自2021年10月1日00:00起至2021年10月7日24:00,新注册并激活(需全部完成账号注册的五个步骤,否则账号状态并未激活)AWS海外区域账户,填写页面下方表单,即可申领价值$200美元的AWS海外区域账户服务抵扣券直充到您的账户,用以抵扣服务消费,助您轻松体验多个云迁移应用场景。同时,您还可获赠AWS精美祥云纪念T恤一件。,仅限7天$20
Wesley13 Wesley13
3年前
Oracle锁的学习
数据库是一个多用户使用的共享资源。当多个用户并发地存取数据时,在数据库中就会产生多个事务同时存取同一数据的情况。若对并发操作不加控制就可能会读取和存储不正确的数据,破坏数据库的一致性。在数据库中有两种基本的锁类型:排它锁(ExclusiveLocks,即X锁)和共享锁(ShareLocks,即
Wesley13 Wesley13
3年前
Java 多线程(一)—— 概念的引入
并发和并行并行:指两个或多个时间在同一时刻发生(同时发生);并发:指两个或多个事件在一个时间段内发生。  在操作系统中,安装了多个程序,并发指的是在一段时间内宏观上有多个程序同时运行,这在单CPU系统中,每一时刻只能有一道程序执行,即微观上这些程序是分时的交替运行,只不过是给人的感觉是同时运行,那是因为分时交替
Wesley13 Wesley13
3年前
Java 之 多线程
一、并发与并行  1、并发指两个或多个事件在同一时间段内发生。  2、并行      指两个或多个事件在同一时刻发生(同时发生)。    !(https://oscimg.oschina.net/oscnet/b4e6bf7c5ec22bb8c38945fff7c188e6
Wesley13 Wesley13
3年前
MySQL中的事务
MySQL中的事务为什么需要事务现在的软件基本上都是多用户、多程序、多线程的,对同一个表可能同时有很多人在用,为保持数据的一致性,所以提出了事务的概念。一个事务一般包含多个操作,这些操作必须当成一个整体来执行,要么都成功,要么都失败,不允许部分成功和部分失败。假如要从A的账户给B的账户转账1000元,那么A的账
Wesley13 Wesley13
3年前
Mysql读写锁及事务
读写锁同一用户并发读取同一条数据,不会出现什么问题,因为读取不会修改数据,但是如果某个用户正在读取某张表,而同一时刻另一用户正在修改这张表的id为1的数据,会产生什么后果?答案是不确定的,读的用户可能会报错退出,也可能读到不一致的数据。 解决这类经典问题的就是并发控制。在处理并发读写的时候,可以通过实现一个由两种类型的锁组成锁系统来解决问题。
Easter79 Easter79
3年前
ThreadLocal可以解决并发问题吗
前言到底什么是线程的不安全?为什么会存在线程的不安全?线程的不安全其实就是多个线程并发的去操作同一共享变量没用做同步所产生意料之外的结果。那是如何体现出来的呢?我们看下面的一个非常经典的例子:两个操作员同时操作同一个银行账户,A操作员存钱,100B操作员取钱50。我们看一下流程。!(https://oscimg.oschina.net/os
ReentrantLock源码解析 | 京东云技术团队
并发指同一时间内进行了多个线程。并发问题是多个线程对同一资源进行操作时产生的问题。通过加锁可以解决并发问题,ReentrantLock是锁的一种。