Redis实现分布式锁

  1. 什么是分布式锁?
    在分布式环境下,系统被拆分,代码可能会被不同的jvm运行,在单进程的情况下,我们可以使用java语言和本身的类库提供的锁,完成高并发的需求。

  2. 常见的分布式锁:

    • Memcached分布式锁
    • redis分布式锁
    • Zookeeper分布式锁
    • Chubby 底层使用了Paxos一致性算法

redis如何实现分布式锁?

三要素:
1.加锁
sentnx命令
setnx(key,1) ,key一般可以用商品ID
当一个线程返回1,说明key不存在;
返回0说明key已存在,抢锁失败

2.解锁
解锁伪代码如下:
del(key)

3.锁超时
锁超时就是说获得锁的线程在运行的时候挂掉了,来不及释放锁
所以设置锁的时候必须设置超时时间。
setnx不支持超时参数,所以有单独的指令:expire(key, 30)
分布式的伪代码如下:

if(setnx(key, 1) == 1) {  //代码执行到这里,线程挂掉
    expire(key, 30)
    try {
        do something...
    } finally {
        del(key)
    }
}
  1. 上面代码在并发的时候会有大问题:
    见注释
    代替指令:set(key, 1, 30, NX)

  2. del误删
    假如A线程锁超时时间是30毫秒,A线程执行的慢,锁释放
    B线程拿到锁,执行,上面释放的可能是B线程的锁,所以加锁的时候要用自己线程的ID作为value,再释放锁的时候验证key对应的value是否属于当前线程。
    加锁:

String threadId = Thread.currentThread.getId();
set(key, threadId, 30, NX);

解锁:

if(threadId.equals(redClient.get(key))) {
    del(key)
}

这里有一个问题,判断和释放锁不是一个原子性操作,需要使用lua脚本实现

"local currentValue = redis.call('get', KEYS[1]);\n" + "if currentValue == ARGV[1]\n" + "then\n"
                + "redis.call('del',KEYS[1]);\n" + "return true;\n" + "end\n" + "return nil;";

3.守护线程
* 获得锁的线程开启守护线程,给快要过期的锁续航,这样A线程就可以完成任务再释放。
* 由于守护线程和A线程在同一个进程中,如果jvm挂掉,这把锁到了超时时间也没人续命,自动释放。

实际运用的代码:

value = redisUtil.tryLock(req.getCustNum()); //加锁 用会员编号作为key
——> tryLock() 方法实现:if (redisManager.setnx(key, value, REDIS_EXPIRE_TIME)) {
                                   return value;
                             }    //返回value,在释放锁时验证释放的是同一个锁

释放锁: 
redisUtil.releaseCommonLock(req.getCustNum(), value);  //和上面value值相同,防止误删锁
——>execDelLuaScript(generateKey(custNum), value, delScript);  //三个参数,key值会员编号,value返回值:可以放当前线程id,第三个参数是lua脚本
——>jedis.eval(luaScript, keys, args);

你可能感兴趣的:(分布式架构)