Redis 分布式锁问题

redis 分布式锁遇到的问题

在分布式锁的应用中,常常通过redis 实现分布式锁,但是遇到一些问题,记录一下。

  1. 锁释放问题
1
2
3
4
5
6
// 获取锁操作
if redis.setnx(lockKey, "lock") != 1 {  // 获取锁成功后会继续执行
  return errors.New("获取锁失败")
}

// business logic ...

问题: 业务逻辑执行完之后锁没有释放,导致后续请求都获取不到锁,导致业务逻辑执行失败。 解决: 在业务逻辑执行完之后,再释放锁。

  1. 死锁问题
1
2
3
4
5
6
7
8
// 获取锁操作
if redis.setnx(lockKey, "lock") != 1 {  // 获取锁成功后会继续执行
  return errors.New("获取锁失败")
}

// business logic ...

redis.del(lockKey) // 释放锁操作失败,导致所没有释放

问题: 获取锁成功后,业务逻辑执行完之后,释放锁失败,导致锁没有释放,导致后续请求都获取不到锁,导致业务逻辑执行失败。 解决方案:获取锁之后,设置锁的过期时间,防止锁没有释放,导致锁无法释放。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
// 获取锁操作
if redis.setnx(lockKey, "lock") != 1 {  // 获取锁成功后会继续执行
  return errors.New("获取锁失败")
}

redis.expire(lockKey, 30) // 设置过期时间,若释放锁失败,则锁会自动释放

// business logic ...

redis.del(lockKey) 
  1. 锁的生命周期设置失败
1
2
3
4
5
6
// 获取锁操作
if redis.setnx(lockKey, "lock") != 1 {  // 获取锁成功后会继续执行
  return errors.New("获取锁失败") 
}

redis.expire(lockKey, 30) // 设置失败

问题:设置有效期时 redis 出现宕机,锁无法释放,导致锁变成死锁。 解决方案:使用 lua 脚本,可以保证 获取锁和设置有效期的原子性。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
  // 加锁
  key := "lock"
  value := uuid.New()
 cmd := redis.NewScript(`
 if (redis.call("setnx", KEYS[1], ARGV[1]) == 1)  then
  return redis.call("expire", KEYS[1], ARGV[2]);
 else 
  return 0;
 end
 `).Run(ctx, rds.rdb, []string{key}, value, exp)
 if ok, _ := cmd.Int64(); ok != 1 {
  return errors.New("set lock failed")
 }

 // business logic
  1. 并发锁被其他进程释放

问题:当进程A业务逻辑还没有执行完,锁已经结束生命周期(expire已过期),此时操作锁释放,其他进程B可以得到锁,在B还没有执行完时,A先执行完,并手动释放锁,此时A进程释放的是B进程的锁。 解决方案:释放锁时,要判断锁是否属于自己,若不是,则不释放,可以将锁的value 设置为uuid,如果是当前进程的uuid,则可以释放。否则不释放。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
key := "lock"
value := uuid.New()

// 加锁 ...

// business logic ...

// 释放锁
 cmd := redis.NewScript(`
 if (redis.call("get", KEYS[1] ) == ARGV[1])  then  
  return redis.call("del", KEYS[1]);
 else 
  return 0;
 end
`).Run(ctx, rds.rdb, []string{key}, value)
 if ok, _ := cmd.Int64(); ok != 1 {
  fmt.Printf("del lock error, err: %v\n", cmd.Err())
  return errors.New("del lock failed")
 }
 return nil
Licensed under CC BY-NC-SA 4.0
皖ICP备20014602号
Built with Hugo
Theme Stack designed by Jimmy