分布式锁-数据库实现
1:for update 的原理
select 检索出的数据, for update 加上了一把锁,其他的人是不能修改这个数据的,也不能在给这个数据加锁。其他线程可以检索出来,但是我在用 for update 再给这些数据加锁是加不上的,因为这个锁呢,已经被前一个线程给锁住了。其他人是不能给它加锁的,在加锁的期间,其他人也不能修改这些数据。因为update 更新数据呢,是要获取这些数据的锁的
2:代码实现
@Transactional(rollbackFor = Exception.class)
@RequestMapping("/database")
public String databaseLock () throws InterruptedException {
log.info("进入方法");
BusinessLock businessLock = businessLockMapper.getLock("demo");
if (ObjectUtils.isEmpty(businessLock)) {
log.info("分布式锁找不到");
}
log.info("进入锁");
Thread.sleep(6000);
log.info("方法执行完成");
return "方法执行完成";
}
mapper 里面的sql
SELECT * FROM business_lock WHERE business_code = #{businessCode} FOR UPDATE
- 开启两个实例,端口分别是 8080 8088
- 下图 8080 应用
- 58:16秒 进入方法
- 58:16秒 进入锁
- 58:22秒 方法执行完成
- 下图 8088 应用
- 58:17秒 进入方法
- 58:22秒 进入锁
- 58:28秒 方法执行完成
在代码中我们让获取到锁的应用休眠6秒 ,8080 和 8088 差不多同一时间进入方法,
但是8080更早一些所有获得到了锁,而8088就在数据库中阻塞,等待获取锁。
在16秒 8080获得锁等待6秒 22秒释放锁,这时 8088才拿到了锁。说明在多个应用中锁是有生效的。
3:用native 测for update
- 先将当前页面的数据库自动提交先关掉
为什么要先关掉呢?你现在检索出来以后或者for update 加了锁,加了锁以后它马上事务呢就会自动的去提交,事务提交了。锁就会自动释放了。其他的会话,还会检索出这条数据来
- 下图:默认是自动提交事务的
- 下图:这时候都是没有关闭自动提交事务的。两者都可以执行加锁页面。说明没有起到我们想要的作用
- 下图:关闭两个窗口的自动提交事务
- 再次检索带有 for update 关键字的sql 。这时候锁被左窗口锁住了,导致右窗口无法加锁。这时候在 左窗口执行commit 提交事务操作,右窗口立马就查询出数据了 。说明锁是生效的
- 左窗口执行 for update ,右窗口不使用 for update 而是直接执行查询 是可以查询出数据的
4:总结