redis 正确实现分布式锁的正确方式

前言

最近在自己所管理的项目中,发现redis加锁的方式不对,在高并发的情况有问题。故在网上找搜索了一把相关资料。发现好多都是互相抄袭的,很多都是有缺陷的。好多还在用redis 的 setnx命令来实现分布式锁。其实redis 中的set命令本身就已经集成了setnx命令的功能了,而且比其还强大。这里,我使用 redis-cli 客户端 结合lua脚本 原生 的实现redis 分布式锁。

准备材料

  • redis-server
  • redis-cli

LUA 与 REDIS 的关系

从 redis2.6.0 开始, redis 内部就内置了lua解释器。lua也就是成了redis扩展的一种解决方案了。
格式:

eval scripts num [keys ...] [argv ...]
  • eval : redis中执行lua脚本的命令
  • scripts : lua脚本,不必一定得是lua函数
  • num : keys 的个数,用于区分 keys 和 argv ;没有keys,则写0
  • keys: 可变数组,一般用于表示redis 的键,下标从1 开始
  • argv: 可变数组,一般用于表示redis 的值,下标从1 开始

redis 与 lua 的交互

分两种情况:

  • redis 执行 lua 脚本 : 使用 redis中的 eval命令 即可
  • lua & redis
    • lua 接收 redis 发送过来的数据 , 在 SCRIPTS中使用 KEYS[] 与 ARGV[]
    • lua 调用redis 命令, 在SCRIPTS中使用 call() 或者 pcall()
    • lua 将结果返回给redis , 使用return

案例解析

1.利用lua 打印参数
127.0.0.1:6379> eval "return {KEYS[1],ARGV[1],ARGV[2]}" 3 key1 key2 first second threed
1) "key1"
2) "second"
3) "threed"

2.通过lua 设置变量
# 设置一个 foo='hello' 键值对
127.0.0.1:6379> eval "return redis.call('set','foo','hello')" 0
OK
127.0.0.1:6379> scan 0
1) "0"
2) 1) "foo"
127.0.0.1:6379> get foo
"hello"
127.0.0.1:6379>
3.通过lua 传参的方式 设置变量
127.0.0.1:6379> eval "return redis.call('set',KEYS[1],ARGV[1])" 1 name Bob
OK
127.0.0.1:6379> keys *
1) "name"
127.0.0.1:6379> get name
"Bob"
127.0.0.1:6379>
4. 利用redis-cli 设置分布式锁
# 加锁 , 直接使用 set nx模式 【生产环境中123456 是一个唯一的随机数】
127.0.0.1:6379> set lock 123456 EX 40 NX
OK

# 解锁 , 结合 lua脚本
127.0.0.1:6379> eval "if redis.call('get','lock') == ARGV[1] then return  redis.call('del','lock') else return 0 end" 0 123456
(integer) 1

127.0.0.1:6379> get lock
(nil)

上面的lua脚本格式化后的样子如下:

if redis.call('get','lock') == ARGV[1]
then
    return redis.call('del','lock')
else
    return 0
end

猜你喜欢

转载自www.cnblogs.com/yinguohai/p/11414779.html
今日推荐