标签:set lock Redis token setnx my 分布式
目录
写在前面
使用Set+lua替代 setnx
1、redis锁
setnx 锁在 redis 中最简单的数据结构就是 string。最早的时候,上锁的操作一般使用 setnx,这个命令是当:lock 不存在的时候 set 一个 val,或许你还会记得使用 expire 来增加锁的过期,解锁操作就是使用 del 命令,伪代码如下:
if (Redis::setnx("my:lock", 1)) {
Redis::expire("my:lock", 10);
// ... do something
Redis::del("my:lock")
}
这里其实是有问题的,问题就在于 setnx 和 expire 中间如果遇到 crash 等行为,可能这个 lock 就不会被释放了。于是进一步的优化方案可能是在 lock 中存储 timestamp。判断 timestamp 的长短。
set 现在官方建议直接使用 set 来实现锁。我们可以使用 set 命令来替代 setnx,就是下面这个样子
if (Redis::set("my:lock", 1, "nx", "ex", 10)) {
... do something
Redis::del("my:lock")
}
上面的代码把 my:lock 设置为 1,当且仅当这个 lock 不存在的时候,设置完成之后设置过期时间为 10。
获取锁的机制是对了,但是删除锁的机制直接使用 del 是不对的。因为有可能导致误删别人的锁的情况。
比如,这个锁我上了 10s,但是我处理的时间比 10s 更长,到了 10s,这个锁自动过期了,被别人取走了,并且对它重新上锁了。那么这个时候,我再调用 Redis::del 就是删除别人建立的锁了。
官方对解锁的命令也有建议,建议使用 lua 脚本,先进行 get,再进行 del
程序变成:
$token = rand(1, 100000);//可替换为 UUID
function lock() {
return Redis::set("my:lock", $token, "nx", "ex", 10);
}
function unlock() {
$script = `
if redis.call("get",KEYS[1]) == ARGV[1]
then
return redis.call("del",KEYS[1])
else
return 0
end
`
return Redis::eval($script, "my:lock", $token)
}
if (lock()) {
// do something
unlock();
}
这里的 token 是一个随机数,当 lock 的时候,往 redis 的 my:lock 中存的是这个 token,unlock 的时候,先 get 一下 lock 中的 token,如果和我要删除的 token 是一致的,说明这个锁是之前我 set 的,否则的话,说明这个锁已经过期,是别人 set 的,我就不应该对它进行任何操作。
所以:不要再使用 setnx,直接使用 set 进行锁实现。
标签:set,lock,Redis,token,setnx,my,分布式 来源: https://blog.csdn.net/mingyuli/article/details/120732660
本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享; 2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关; 3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关; 4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除; 5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。