Code前端首页关于Code前端联系我们

mysql更新锁实现代码,详细讲解mysql加锁过程

terry 2年前 (2023-09-30) 阅读数 40 #Mysql
文章标签 Mysql

本文内容列表:

  • 1. MySQL——用于行锁定表锁定更新
  • 2、如何用Java实现Mysql数据库的行锁
  • 3.使用MySQL实现分布式锁,你听说过吗?

MySQL - 用于行锁定表锁定更新

update的

作用是在轮询时对该行加排他锁。当一个事务未完成时,其他事务可以读取,但不能写入或更新。

其典型使用场景是高并发,对资金、库存等数据精度要求较高。一般这种操作就是排长队,启动一个事务。现在如果要对 inventory 进行操作,第一次读的时候是 1,然后另外一个进程立刻将 inventory 更新为 0,但是事务还没有完成,后面的逻辑总是用 1,这样就会出现问题,所以你必须在更新上使用锁来避免这种情况的发生。出了些问题。

行锁的具体实现算法有3种:记录锁、空间锁、下一个键锁。

仅针对可重复读或隔离级别以上的特定操作,才会获得空格锁或下一键锁。在select、update和delete过程中,除了基于唯一索引的查询之外,还会检索其他索引查询。间隙锁定或下一键锁定,锁定其扫描范围。主键索引也是唯一索引,因此主键索引不会使用空间锁或另一个键

锁来更新。仅适用于InnoDB,并且事务必须在start和commit之间启用才能生效。

select语句默认不获取任何锁,因此可以通过排他锁读取其他事务持有的数据!

InnoDB 同时实现行锁和表锁。

当明确指定主键/索引时,是行级锁,否则是表级锁。

假设表user有id和name字段,id为主键,有5条数据。

如果明确指定主键且存在该记录,则行级锁

无主键/索引,表级锁

如果主键/索引不明确,则表级锁

显式如果不存在,则提供主键/索引 这条记录是无锁的

参考博文:

如何用 Java 实现 Mysql 数据库的行锁

以下以图为例

场景如下:

用户账户有余额,当发生交易时,需要实时更新余额。如果这里出现并发问题,就会导致用户余额和实际交易不匹配,这对于公司和客户来说都是非常危险的。

如何避免:

经网上核实,有两种方法:

1。使用悲观锁

当需要改变余额时,通过更新行锁定代码设置事务中当前需要更新的记录,然后运行正常的查询和更新操作

这样其他事务就可以等待交易完成后即可操作

当然要特别注意。如果使用 Spring 事务注解,需要配置:

!--(事务管理)事务管理器,使用 JtaTransactionManager 进行全局 tx --

bean id="transactionManager"

class="org. springframework.jdbc.datasource.DataSourceTransactionManager"

property name="dataSource" ref="dataSource" /

/bean

!--使用注解定义事务--

ttransaction -manager="transactionManager " /

在给定代码处添加事务注释

@Transactional

@Override

public boolean raiseBalanceByLock(Long userId, Bigdate Exdeci´”) long time = System.currentTimeMillis();

/ /获取日志锁

UserBalance Balance = userBalanceDao.getLock(userId);

LOGGER.info("[锁定]开始时间:{}", time );

if (null == Balance) {

抛出新的 ValidateException(

ValidateErrorCode.ERRORCODE_BALANCE_NOTEXIST,

"用户余额不存在");

}

boolean result = userBalanceDao.increaseBalanceByLock(balance, amount);Systemlis =long.End LOGGER.info("[lock ] end . time: {}", timeEnd);

返回结果;

}

MyBatis 中的Lock方法。在实际测试中,这种方法可以非常有效地控制,而且是大规模的。并发可能会导致性能问题

select id="getLock" resultMap="BaseResultMap"parameterType="java.lang.Long"

![CDATA[

select * from user_balance where id=# {id,jdbcType =BIGINT} 更新;

]]

/选择

2。使用乐观锁

这个方法也可以解决场景描述的问题(我觉得是不频繁操作比较合适):

在设计表的时候添加版本(版本控制字段)。每次您需要更新余额时,请先获取该项目。更新时根据版本和ID情况进行更新。如果更新返回的数字为0,说明版本发生了变化

需要重复更新操作,如下:sql脚本

update user_balance set Balance = #{balance,jdbcType=DECIMAL},Version = Version +1,其中 Id = #{id ,jdbcType=BIGINT} 且版本 = #{version,jdbcType=BIGINT}

是一种不使用数据库锁的方法,解决方案也非常巧妙。当然,在并发量较大的情况下,一次推演需要重复多次才能成功,这还是有弊端的。不知道有没有更好的办法。

使用MySQL实现分布式锁,你听说过吗?

以前我参与过库存系统。由于其业务复杂性,我构建了许多应用程序来支持它。在这种情况下,一个库存数据可以有多个应用程序同时编辑该库存数据。

例如,有一个计划任务域xx.cron和多个JAVA应用程序,例如SystemA域和SystemB域,它们可以同时修改相同的库存数据。如果不协调,就会出现脏数据。

您可以使用DB或Redis等外部环境来协调JAVA进程之间的线程。下面文字介绍如何使用DB实现分布式锁。

本文提出的分布式锁交互模式如下:

使用synchronized关键字时,必须指定锁对象。

进程内的线程可以基于vol进行同步。这里的obj可以理解为锁对象。如果一个线程想要进入同步代码块,它必须首先持有 obj 对象的锁。这种锁是JAVA中内置的锁,创建过程是线程安全的。那么如何借助DB使锁创建过程线程安全呢?

您可以在DB中使用UNIQUE KEY功能。一旦遇到重复的key,由于UNIQUE KEY的唯一性,就会抛出异常。在 JAVA 中,它是 SQLIntegrityConstraintViolationException。

transaction_id 是交易 ID。例如,您可以使用

构造一个 transaction_id,表示特定仓库的特定销售模型中的特定条形码源。当然,不同的条形码具有不同的交易标识符。如果有两个应用,则创建同一个transaction_id的锁资源时,只能成功创建一个应用。

在写操作频繁的业务系统中,通常会进行分库,以减轻单个数据库的写入压力,提高写操作的吞吐量。如果使用分库,业务数据自然会分布到各个数据库。

在这个水平分区的多数据库上使用分布式数据库锁,您可以自定义数据源列表。并暴露getConnection(String TransactionId)方法,通过TransactionId找到对应的连接。

实现代码如下:

首先编写initDataSourceList方法,使用Spring的PostConstruct注解初始化DataSource列表。相关的数据库配置是从db.properties加载的。

数据源使用阿里巴巴DruidDataSource。

接下来最重要的是实现getConnection(String TransactionId)方法。实现原理很简单,只需要获取交易的hashcode并对DataSource的长度取模即可。

创建连接区域列表后,您可以将数据插入到distribution_lock表中。

接下来,使用DB update select函数来锁定线程。当多个线程同时处理基于同一事务标识符的更新选择时,只有一个线程可以成功,其他线程将被阻塞。所有线程之一将被阻塞,直到成功的更新选择线程使用提交操作。只有这样我们才能开始工作。

在上面的DistributedLock类中,我们创建了一个lock方法。

当线程完成任务后,必须手动解除阻塞,才能让之前锁定的线程继续工作。在我们上面的实现中,实际上是获取当时成功选择更新的线程对应的连接,并执行提交操作。

那么如何获得呢?我们可以使用ThreadLocal。首先,在 DistributedLock 类中定义

。每次调用lock方法时,连接都会被放置在ThreadLocal中。我们将调整锁定方式。

这样,当获得连接时,它被设置为ThreadLocal。如果锁定方法中发生异常,则会从 ThreadLocal 中抛出异常。

完成这些步骤后,我们就可以进行解锁操作了。我们为DistributedLock添加一个unlock方法。

毕竟DB是用来实现分布式锁的,这毕竟给DB带来了一些压力。我们当时考虑使用数据库进行分发的一个重要原因是我们的应用程序是后端应用程序,通常不会看到太多流量。相反,关键是要保证库存数据的正确性。对于前端库存系统这样的操作,比如添加购物车填充库存,最好不要使用DB来实现分布式锁。

如何锁定多份数据?例如,某个库存操作需要同时调整物理库存和虚拟库存。您想要同时锁定物理和虚拟库存。其实并不难。看lock方法,编写multiLock方法,为多个事务标识符提供输入参数,并在for循环中处理它们。稍后有时间我会补上的。

版权声明

本文仅代表作者观点,不代表Code前端网立场。
本文系作者Code前端网发表,如需转载,请注明页面地址。

热门