基于数据库实现分布式锁
- 多个进程/多个线程访问共同组件数据库
- 通过select…for update 访问同一条数据,for update锁定数据,其他线程只能等待
Select For update语句
该语句用来锁定特定的行(如果有where子句,就是满足where条件的那些行)。当这些行被锁定后,其他会话可以选择这些行,但不能更改或删除这些行,直到该语句的事务被commit语句或rollback语句结束为止。
mysql默认是自动提交事务的,将窗口1设置为手动提交事务
在窗口1中执行
select * from product where id = '100100' FOR UPDATE
在窗口2中执行,由于窗口1中并没有提交事务,行锁还没有释放。所以窗口2上锁失败
窗口1执行
COMMIT;
此时窗口2可以正常上锁
代码验证
@RequestMapping("singleLock")
@Transactional(rollbackFor = Exception.class)
public String singleLock() throws Exception {
log.info("我进入了方法!");
DistributeLock distributeLock = distributeLockMapper.selectDistributeLock("demo");
if (distributeLock==null) throw new Exception("分布式锁找不到");
log.info("我进入了锁!");
try {
Thread.sleep(20000);
} catch (InterruptedException e) {
e.printStackTrace();
}
return "我已经执行完成!";
}
数据库查询语句
<select id="selectDistributeLock" resultType="com.example.distributelock.model.DistributeLock">
select * from distribute_lock
where business_code = #{businessCode,jdbcType=VARCHAR}
for update
</select>
启动两个jvm(9999和9998)
分别请求localhost:9998/singleLock和localhost:9999/singleLock
9998的请求拿到数据库表中的某一行数据的行锁之后,会休眠20秒,此时9999的请求进来之后,由于行锁没有释放,只能进行等待
localhost:9998/singleLock
localhost:9999/singleLock
20s之后,9999会获取锁