【for update】解析 【线程安全】【分布式锁】

1. for update是什么?

for update是一种DML操作级别的行级锁。在结合事务的场景中,事务A对一行数据使用select … for update加锁,其他事务只能查询但不能更新被加锁的数据行,而事务A既可以查询又可以更新该行数据。事务A提交后,其他事务才可以更新该行。

2. for update有哪些应用场景?

2.1.实现线程安全

后端服务运行时,由于存在网络延时,可能会出现上游调用方先后发出两次请求(假设为req_1、req_2)来更新同一行数据的同一个字段的情况。如果后端服务正在处理req_1,在没有拿到req_1的执行结果时直接处理req_2,会产生错误的结果。因此,需要一种线程安全机制来保障只能有一个请求来更新数据。

线程安全机制:

1)在单机版服务上,可以使用synchronized关键字在代码中手动加锁,从而避免多个线程重复更新一行数据,但是synchronized关键字不能解决分布式场景中的并发问题。因为synchronized关键字是在java代码层面中加锁,分布式服务具有多个实例,每台实例不感知其他实例的synchronized关键字。

2)在分布式服务中,可以使用redis实现分布式锁,如果req_1、req_2要申请同一把锁,那么只有后端服务处理完req_1后,req_2才能获取锁进而被服务处理。分布式锁发挥作用的级别在于线程级,服务器接收到一个请求后,会为其分配一个专用的线程,该线程执行某个方法时,会在代码中显式地申请和释放分布式锁(如以自定义分布式锁的注解和切面)。

3)分布式服务中,也可以使用for update实现线程安全。for update发挥作用的级别在于事务级,要比分布式锁更靠近数据底层。结合事务,我们不需要再代码中手动加锁,当处理req_1的线程进入一个事务方法,该方法内使用for update为要更新的数据加锁后,其他事务只能查询无法更新被锁的数据。

2.2.主从数据库结构下查询主库

在数据库具有主从结构时,如果一个java方法注解成事务,在事务中for update能够对数据加锁;如果使用了for update的方法没有事务,那么查询完数据后,for update的锁就会被释放。唯一的用处是该条数据是从主库查出来的,能够避免主从延迟对查询数据准确性的影响。

3.for update的优点和弊端?

3.1.优点:

1)能够解决分布式服务中synchronized关键字不能保障线程安全的问题,同时避免使用分布式锁(可能有点麻烦)

3.2.弊端:

1)mysql中的for update,只适用于InnoDB引擎,必须结合事务才能在事务内对数据加锁

2)for update虽然是行级锁,但是在where条件没有命中索引的情况下会锁表

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
Spring Cloud提供了多种实现分布式锁的方案,其中一种常见的方案是基于数据库实现分布式锁。 在基于数据库实现分布式锁的方案中,我们可以使用数据库的乐观或悲观来实现分布式锁。具体步骤如下: 1. 创建一个数据库表,用于存储的信息,例如的名称、持有者、过期时间等字段。 2. 当需要获取时,通过数据库的乐观或悲观机制来保证只有一个线程能够成功获取到。 3. 获取的线程在数据库中插入一条记录,表示该线程持有了该。 4. 其他线程在尝试获取时,会发现该已经被其他线程持有,从而无法获取到。 5. 当持有的线程完成任务后,释放,即在数据库中删除该记录。 以下是一个使用基于数据库实现分布式锁的示例代码: ```java @Service public class DistributedLockService { @Autowired private JdbcTemplate jdbcTemplate; public boolean tryLock(String lockName, String holder, long expireTime) { try { // 使用乐观机制,通过更新操作来获取 int updatedRows = jdbcTemplate.update("UPDATE distributed_lock SET holder = ?, expire_time = ? WHERE lock_name = ? AND (expire_time IS NULL OR expire_time < ?)", holder, expireTime, lockName, System.currentTimeMillis()); return updatedRows > 0; } catch (Exception e) { // 处理异常 return false; } } public void releaseLock(String lockName, String holder) { try { // 释放,即删除数据库中的记录 jdbcTemplate.update("DELETE FROM distributed_lock WHERE lock_name = ? AND holder = ?", lockName, holder); } catch (Exception e) { // 处理异常 } } } ``` 使用该示例代码,你可以在需要加的地方调用`tryLock`方法来尝试获取,如果返回`true`表示获取成功,然后执行任务;任务完成后,调用`releaseLock`方法来释放
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值