【分布式基础】分布式锁

分布式锁

为什么需要分布式锁

当我们遇到同一方法在同一时间只能让一个线程来执行,那么在单机环境下,我们不需要考虑分布式锁,只需要使用 Java 相应的 API 即可。但是当我们的系统是分布式部署的时候,就不能仅在线程方面考虑锁的问题了,分布式环境下与单机环境最大的不同不是多线程而是多进程

多线程由于可以共享堆内存,因此可以简单的采取内存作为标记存储位置,而进程之间可能彼此都不在一个物理机上,因此需要将标记存储在一个所有进程都能看得到的地方。

基于数据库的分布式锁

基于数据库表

要使用数据库实现分布式锁,最简单的就是创建一个锁表,然后通过该表中的数据实现获取和释放锁的操作。

当我们要锁住某个方法或者某个资源的时候,我们就在该表中加入一条记录,想要释放的时候就删除这条记录。

可以创建一张表:

CREATE TABLE `resource_lock` (
  `key_resource` varchar(45) COLLATE utf8_bin NOT NULL DEFAULT '资源主键',
  `status` char(1) COLLATE utf8_bin NOT NULL DEFAULT '' COMMENT 'S,F,P',
  `lock_flag` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '1是已经锁 0是未锁',
  `begin_time` datetime DEFAULT NULL COMMENT '开始时间',
  `end_time` datetime DEFAULT NULL COMMENT '结束时间',
  `client_ip` varchar(45) COLLATE utf8_bin NOT NULL DEFAULT '抢到锁的IP',
  `time` int(10) unsigned NOT NULL DEFAULT '60' COMMENT '方法生命周期内只允许一个结点获取一次锁,单位:分钟',
  PRIMARY KEY (`key_resource`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin

当我们想要加锁就执行相应的 insert 语句,解锁就执行相应的 delete 语句

这张表之所以可以用来当做分布式锁,主要使用的是 主键的唯一性,如果有针对同一资源的加锁请求同时提交到数据库的话,数据库会保证只有一个操作可以执行成功。

存在的问题

  1. 锁强依赖数据库的可用性,若数据库是一个单点,数据库挂掉,整个系统就不能用了
  2. 锁没有失效时间,一旦解锁失败,记录会一直在数据库中,其他线程无法获得到锁
  3. 锁是非阻塞的,数据库 insert 失败只会直接报错,没有获得锁的线程不会进入队列排队等待
  4. 锁是非重入的

基于 Redis 实现分布式锁

利用 Setnx + expire命令(错误做法)

Redis 的 Setnx key value 命令可以用来当 key 不存在时才能成功添加新值,若 key 存在就什么也不做,考虑超时机制,所以我们利用 expire 命令来设置超时时间

public boolean lock(String key,String value,int timeout) {
    Long result = jedis.setnx(key, value);
    if (result == 1) {
        return jedis.expire(key, timeout) == 1;
    } else {
        return false;
    }
}

但是因为 setnx 和 expire 是分开操作的,并不是原子操作,如果 setnx 指令做完了就宕机了,那么这个锁就不会过期了

利用 Lua 脚本

针对上面的问题,我们可以使用 Lua 脚本解决,将 sentnx 和 expire 操作写进一个 Lua 脚本中,即可实现原子操作。

使用 set key value [EX sencond] [PX millisecond] [NX|XX]命令

这个命令是一个原子操作

public boolean tryLock_with_set(String key, String UniqueId, int seconds) {
    return "OK".equals(jedis.set(key, UniqueId, "NX", "EX", seconds));
}

value 需要具备唯一性,这样可以防止其他线程将自己的锁删除掉

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值