基于数据库实现分布式锁

1.基于数据库表的实现

首先创建一张锁表

这里将方法名做唯一性约束

当我们想锁住某个方法时,可以根据方法名insert一条语句

INSERT INTO `aa`.`t_lock` (`t_id`, `t_method`, `t_created_time`, `f_memo`) VALUES ('1', 'payMethod', '2019-01-09 15:26:27', '支付方法');

这时如果有另外一个线程要调用支付的方法,由于违反了数据库唯一性的约束,会抛出异常,因此可以认定为拿不到锁,当拿到锁的方法执行完业务代码后,最后释放锁的时候

再执行一条delete语句就行。

扫描二维码关注公众号,回复: 4996870 查看本文章
delete from t_lock where t_method = "payMethod"

这个案例中使用method作为唯一性约束,实际总可根据具体业务自行定义,例如排期案例中,即可选取日期作为主键id,主键本身满足唯一性,同时每天的日期也是不一样的。

问题

1、数据库是一个单点,一旦数据库挂掉,会导致业务系统不可用。

2、这把锁没有失效时间,一旦解锁操作失败,就会导致锁记录一直在数据库中,其他线程无法再获得到锁。

3、这把锁只能是非阻塞的,因为数据的insert操作,一旦插入失败就会直接报错。没有获得锁的线程并不会进入排队队列,要想再次获得锁就要再次触发获得锁操作。

4、这把锁是非重入的,同一个线程在没有释放锁之前无法再次获得该锁。因为数据中数据已经存在了。

解决

1.数据库可以考虑主备方式,主的一旦挂了,即可切换到从库。

2.可以在表里加一个失效时间的字段,通过另外一个定时任务工程扫表,判断过期时间来释放锁。

3.可以采用while循环方式,如果插入失败,捕获异常,同时在外层加个判断,如果没有拿到锁,继续执行,直到拿到锁的线程的释放锁,继续执行业务。

4.可以在表里再加个2个字段,譬如1个是主机ip,和当前线程,当线程锁住时同时插入这两个字段,如果要想拿到锁,根据线程名字和主机ip来查询,如果查询到了,直接分配。

最终数据表方案设计如下

CREATE TABLE `t_lock` (
  `t_id` int(11) NOT NULL,
  `t_method` varchar(255) NOT NULL COMMENT '锁定的方法名',
  `t_created_time` datetime NOT NULL COMMENT '创建时间',
  `f_memo` varchar(255) DEFAULT NULL COMMENT '备注',
  `f_host_ip` varchar(255) NOT NULL COMMENT '主机ip',
  `f_thread_name` varchar(255) NOT NULL COMMENT '线程名',
  PRIMARY KEY (`t_id`),
  UNIQUE KEY `method_index` (`t_method`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

2.基于数据库排他锁

利用数据库的唯一性约束,通过增删来加锁解锁是一种方案,同时利用数据库自带的排他锁也能实现分布式锁。

同样参考上表

原始sql

select  * from  t_lock where t_method = "payMethod";

现在后面加上 for  update

public boolean lock() {
   //开启事务
     begin;
    
    while (true) {
       try {
      
       string result = "select  * from  t_lock where t_method = "payMethod" for update;

        if (result == null) {
      
            return true;
       }

    
       }
       catch(Exception e) {

       }

    }
}
public void unock() {
//提交事务,释放排他锁
commit();
}

问题:

阻塞锁? for update语句会在执行成功后立即返回,在执行失败时一直处于阻塞状态,直到成功。

锁定之后服务器宕机,无法释放?使用这种方式,服务宕机之后数据库会自己把锁释放掉。

同样还是无法直接解决数据库单点 还有可重入问题。

 总结:

优点

               直接借助数据库,容易理解。

缺点

            会出现的问题多,在解决问题的过程中会使整个方案变得越来越复杂。

           操作数据库需要一定的开销,性能问题需要考虑。不建议和业务表放在一个数据库实例

            使用数据库的行级锁并不一定靠谱,尤其是当我们的锁表并不大的时候。

猜你喜欢

转载自www.cnblogs.com/edison20161121/p/10295087.html