分布式锁MySQL实现

分布式系统中,一个避不开的话题,就是在很多情况下,我们需要用到分布式锁。
那分布式锁,通常有哪几种实现方式呢?
分布式锁的实现方式,通常有三种,数据库实现,Redis实现,Zookeeper实现。
我们将分三篇文章来分别介绍这三种实现。
首先要来介绍的是数据库版实现的分布式锁。我们看以下几个场景中,如何用mysql数据库来实现一个分布式锁。

场景1:我们曾经做的一个贷款系统,在用户借款前,需要给用户开一个还款的账户。还款账户一个用户只需要开一个就够了,但线上使用时,偶尔会出现一个用户开了多个还款账户的情况,原因就是一个客户同时借了几笔款,出现了并发。
出现并发的代码逻辑大概是这样的:
step1:查询用户A是否开过还款账户。
select * from acct_info where userid = ‘用户A’。
step2:如果step1查到用户A已开户,则return,不再开户。
如果step2查到用户A未开户,则去调用开户接口开户。
当step1执行查询时,刚好有两个线程并发,线程A,线程B同时去查询是否开过户,这时线程A,线程B查询到的结果都是未开户,都去执行开户逻辑,就会开出两个户,这是我们不希望看到的。
那针对场景1的这种情况,我们如何用mysql通过实现一个分布式锁来解决呢?
这种场景,我们可以用mysql的唯一索引来解决。
我们可以建一张acct_control表,里面有一个userid字段,并给该字段建一个唯一索引。在开户前,我们先往acct_control表中插入一条数据,insert into acct_control (userid) values (‘用户A’),如果插入成功,就认为获取到了锁,然后可以去执行开户逻辑。如果出现场景1中并发的情况,有两个线程同时往acct_control表中写数据,因为我们在userid上建了一个唯一索引,那么必然只会有一个线程写入成功,一个线程写入失败。
写入成功的我们认为是获取到了锁,可以去执行开户流程,写入失败的,我们认为没有获取锁,不能去开户。
场景2:我曾经做过的一个银行系统中有预约转账的功能,比如现在是5.1号,客户预约一个礼拜后的5.7号进行把平安银行账户上的钱转到农业银行去还房贷。5.1号客户操作后,我们会先记录一个预约流水,到了5.7号会通过一个定时任务,把客户的预约流水捞出来进行转账操作。这里需要用到任务调度功能,在分别式系统中,我们为了保证系统的高可用,通常在多台实例上,都会部署调度任务。但预约转账的job真正只能被一台实例去执行,这里也涉及到分布式锁的实现。我们看下,这种情况下,我们如何用mysql来实现一个分布式锁。
我们可以建一张,定时任务加锁表,task_lock_table。在该表中有如下几个字段。
task_lock_table(
task_id varchar(50) primary key, --task_id
task_name varchar(100), --task名称
lock_status varchar(10), --锁状态 LOCK(已被锁), UNLOCK(未被锁)
version varchar(10), --当前锁版本号
modify_time varchar(20) --修改时间
)
我们给这张表进行初始化一下:
insert into task_lock_table values
(‘appoint_transfer’,‘预约转账’,‘UNLOCK’,‘0’,‘1977-12-31 00:00:00’)
假设我们的job服务器共有三台,这三台服务器同时启动,要去执行预约转账的任务,
我们只能让其中的一台去真正的执行转账的服务。我们怎么控制呢?
step1:先获取锁,查询task_lock_table,看下lock_status状态。
A:如果lock_status状态为UNLOCK,则认为锁未被占用,去获取该锁,同时也需要获取当前的version,假如version=‘0’。
update task_lock_table set lock_status = ‘LOCK’, modify_time = now(), version=version+1 where task_id = ‘appoint_transfer’ and version=‘0’,
如果update成功,则成功获取该锁。可以去执行对应的转账的操作。
如果update没有更新成功,则认为没有获取到锁,不能执行转账操作。
B:如果lock_status状态为LOCK,则认为锁已被占用。这时又分两种情况:
b1: 如果modify_time和当前时间差小于超时时间,则认为该锁是被占用,获取锁失败,不执行转账任务。
b2:如果modify_time和当前时间差大于超时时间,则认为该锁未被占用(锁已被释放),去获取该锁,查询获取当前version=‘1’。
update task_lock_table set lock_status = ‘LOCK’, modify_time = now(), version=version+1 where task_id = ‘appoint_transfer’ and version=‘1’。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值