分布式锁原理

应用场景

在业务传统场景下,单机版本的并发控制我们可以利用sychronizedretrantLock来完成,但是在集群环境下,不同的的JVM如何完成并发控制呢?这就需要利用分布式锁。

实现方式

1.  基于数据库的唯一约束实现
2.  基于缓存如Redis实现
3.  基于ZooKeeper实现

一 基于数据库的实现

实现思路:对lockMethod施加唯一约束

DROP TABLE IF EXISTS `c_table_lock_info`;
CREATE TABLE `c_table_lock_info` (
  `id` int(11) NOT NULL AUTO_INCREMENT,,
  `lockMethod` varchar(64) DEFAULT NULL COMMENT '锁定的方法名',
  `remark` varchar(255) DEFAULT NULL COMMENT '备注信息',
  `addTime` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
  `updTime` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
  PRIMARY KEY (`id`),
  UNIQUE KEY `uidx_lockMethod` (`lockMethod`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8 COMMENT='系统锁信息';

在之后每次遇到需要加锁的方法的时候,先执行以下SQL

INSERT INTO c_table_lock_info(lockMethod,remark) VALUES('加锁的方法名','方法备注');

如果多个请求都需要调用此方法,当其中某一个请求插入成功时,即可认为该请求获取了锁,完成相应的业务逻辑,在处理完之后,调用以下SQL

DELETE FROM c_table_lock_info WHERE lockMethod='加锁的方法名';

此时,锁被释放,可以再次被使用

但是存在一些缺点急需优化

1.  基于数据库实现,数据库的性能会直接影响锁的性能
2.  不具备可重入的性质,若在未释放锁之前,该请求再次调用此方法,导致无法插入数据,获取锁失败,需要加相应的认证信息,确认你还是你,但你还是你的时候,直接把锁给你
3.  锁没有相应的失效机制,在服务宕机之后,数据库中仍然存在该数据,当请求再次过来的时候,没有请求能获取到锁,需要加上失效时间,一段时间之后,锁自动失效
4.  没有阻塞特性,需要优化获取锁的代码逻辑,不断的循环重试去获取锁

二 基于缓存如Redis的实现

Redis是单线程的,也就是说存在很多客人来放松的时候,永远只有一个来处理客人要求的各种服务

实现思路:基于其实现的分布式锁思路就是利用原子命令来完成public String setex(final String key, final int seconds, final String value) {

在保存某一个key的时候并为其设置expirTime

    return (String)(new JedisClusterCommand<String>(this.connectionHandler, this.maxAttempts) {
        public String execute(Jedis connection) {
            return connection.setex(key, seconds, value);
        }
    }).run(key);
}

为了保证在这种情况下的可重入性,仍然需要编写代码封装set

import redis.clients.jedis.Jedis;
import java.util.HashMap;
import java.util.Map;

/**
 * @Description
 * @Author taren
 * @DATE 2019/8/22 16:29
 */
public class RedisLockTest01 {

private ThreadLocal<Map> lockers = new ThreadLocal<>();

private Jedis jedis;

public RedisLockTest01(Jedis jedis) {
    this.jedis = jedis;
}

private boolean _lock(String key) {
    return jedis.set(key, "", "nx", "ex", 5L) != null;
}

private void _unlock(String key) {
    jedis.del(key);
}

private Map<String, Integer> currentLockers() {
    Map<String, Integer> refs = lockers.get();
    if (refs != null) {
        return refs;
    }
    lockers.set(new HashMap<>());
    return lockers.get();
}

public boolean lock(String key) {
    Map refs = currentLockers();
    Integer refCnt = (Integer) refs.get(key);
    if (refCnt != null) {
        refs.put(key, refCnt + 1);
        return true;
    }
    if (!this._lock(key)) {
        return false;
    }
    refs.put(key, 1);
    return true;
}

public boolean unlock(String key) {
    Map refs = currentLockers();
    Integer refCnt = (Integer) refs.get(key);
    if (refCnt == null) {
        return false;
    }
    refCnt -= 1;
    if (refCnt > 0) {
        refs.put(key, refCnt);
    } else {
        refs.remove(key);
        this._unlock(key);
    }
    return true;
}
}

当然setnx的加锁方式也是有缺陷的,完全可以编写Lua脚本来实现,用redis的connection对象去执行脚本

三 基于ZK的实现

实现思路:利用ZK的目录树特性结构,即同一目录下只能有唯一的文件名

1.  创建一个用来实现锁的目录lock
2.  A线程获取锁,就在lock目录下创建顺序节点
3.  遍历lock目录,获取所有的子节点,A发现自己就是最小节点,获得锁
4.  B线程遍历lock目录,发现自己不是最小节点,因为此时A才是最小节点,B对A设置监听
5.  A使用完之后,删除节点,此时B发现自己是最小节点,B获得锁

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 205,236评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,867评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,715评论 0 340
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,899评论 1 278
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,895评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,733评论 1 283
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,085评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,722评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,025评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,696评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,816评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,447评论 4 322
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,057评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,009评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,254评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,204评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,561评论 2 343

推荐阅读更多精彩内容