分布式锁

加锁

主要通过Lua脚本

"if (redis.call('exists', KEYS[1]) == 0) then " +
  "redis.call('hset', KEYS[1], ARGV[2], 1); " +
  "redis.call('pexpire', KEYS[1], ARGV[1]); " +
  "return nil; " +
"end; "
KEYS[1] 参数是:“anyLock” ARGV[2] 是:“id + ":" + threadId”
  1. 首先用的exists 判断redis中是否存在当前key,如果不存在就等于0
  2. 然后执行hset指令,将“anyLock id:threadId 1”存储到redis中

可重入

实现可重入的原理

  • Redis存储锁的数据类型是 Hash类型
  • Hash数据类型的key值包含了当前线程信息。

hashMap结构:<key,<key1,value>>

"if (redis.call('hexists', KEYS[1], ARGV[2]) == 1) then " +
  "redis.call('hincrby', KEYS[1], ARGV[2], 1); " +
  "redis.call('pexpire', KEYS[1], ARGV[1]); " +
  "return nil; " +
"end; " +
"return redis.call('pttl', KEYS[1]);",

如果同一个机器同一个线程再次来请求, value + 1;解锁时 value - 1

https://tva1.sinaimg.cn/large/007S8ZIlly1ggloszfm95j30zk0ozdhf.jpg

readwritelock

第一次加读锁

锁为空,设置 anyLock 的 key、value和过期时间

anyLock: {
  "mode": "read",
  "UUID_01:threadId_01": 1
}

第二次加读锁

锁不为空,value + 1,设置过期时间

anyLock: {
  “mode”: “read”,
  “UUID_01:threadId_01”: 2
}
{anyLock}:UUID_01:threadId_01:rwlock_timeout:1  1
{anyLock}:UUID_01:threadId_01:rwlock_timeout:2  1
  • 读锁与读锁非互斥
  • 读锁与写锁互斥
  • 写锁与写锁互斥
  • 读读、写写 同个客户端同个线程都可重入
  • 先写锁再加读锁可重入
  • 先读锁再写锁不可重入