> 文章列表 > Redisson 分布式锁(基于v1.3.1)

Redisson 分布式锁(基于v1.3.1)

Redisson 分布式锁(基于v1.3.1)

Redisson 分布式锁

v1.0.0版本问题

v1.0.0版本的实现在持有锁的JVM或者持有锁的线程挂掉没有释放锁时,该锁不会被释放并且会一直占用,这个时候就使用DEL命令手动删除。

问题解决

v1.3.1版本通过key的ttl解决了这个问题,关键加锁逻辑改为了Lua脚本

加锁

,关键逻辑:

  1. 根据key名称获取key的value数据
  2. 如果value信息返回false,则代表key不存在,调用redis.call设置key和value并且设置key的超时时间为用户设置的时间或默认超时时间,默认超时时间为30秒。
  3. 如果value存在则json解码获取o字段(o字段表示锁的owner),如果持有锁的JVM或线程是当前线程则将c(c字段为JVM或线程持有锁/获等待锁的计数)加1
  4. 最后返回当前Key的TTL
local v = redis.call('get', KEYS[1]); 
if (v == false) then redis.call('set', KEYS[1], cjson.encode({['o'] = ARGV[1], ['c'] = 1}), 'px', ARGV[2]); return nil; 
else local o = cjson.decode(v); if (o['o'] == ARGV[1]) then o['c'] = o['c'] + 1; redis.call('set', KEYS[1], cjson.encode(o), 'px', ARGV[2]); return nil; " +end;return redis.call('pttl', KEYS[1]); 
end

解锁

  1. 先检查锁Key是否存在,不存在返回OK,同时发布消息(问题:它想解锁而此时锁却不存在了,这是不是有可能已经出事了)
  2. 锁KEY还存在就检查一下owner是不是自己,如果是自己就将计数减1,如果计数还不为0就为锁续期;如果计数为0就删除锁KEY同时发布事件。
local v = redis.call('get', KEYS[1]); 
if (v == false) then redis.call('publish', ARGV[4], ARGV[2]);  return 'OK';  
else  local o = cjson.decode(v);  if (o['o'] == ARGV[1]) then  o['c'] = o['c'] - 1;  if (o['c'] > 0) then  redis.call('set', KEYS[1], cjson.encode(o), 'px', ARGV[3]);return 'FALSE';else  redis.call('del', KEYS[1]); redis.call('publish', ARGV[4], ARGV[2]);  return 'OK';end end; return nil;  
end

Lua脚本返回’OK’,'FALSE’和nil的区别:

  • OK: 表示成功解锁已经被成功解锁,因为锁不存在或已经删除?
  • FALSE: 此次解锁成功, 但是锁并没有被删除,因为unlock的次数还不够?
  • nil: 当前线程并没有持有该锁,你为什要解锁?给你个异常让你尝尝!

问题

  1. 高负载下锁无法续期该怎么办?
  • 不管它,由它去
  • 系统容量预估,不要让你的系统中的组件有那么大的压力
  • 突发情况下,万一某些业务或请求真的出问题了及时告警,业务部门该买单的就买单吧