redis 是 php 的好朋友,在 php 写业务过程中,有时候会使用到锁的概念,同时只能有一个人可以操作某个行为。这个时候我们就要用到锁。锁的方式有好几种,php 不能在内存中用锁,不能使用 zookeeper 加锁,使用数据库做锁又消耗比较大,这个时候我们一般会选用 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);

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 进行锁实现。

												

解锁 redis 锁的正确姿势的更多相关文章

  1. 解锁redis锁的正确姿势

    解锁redis锁的正确姿势 redis是php的好朋友,在php写业务过程中,有时候会使用到锁的概念,同时只能有一个人可以操作某个行为.这个时候我们就要用到锁.锁的方式有好几种,php不能在内存中用锁 ...

  2. 【分布式缓存系列】Redis实现分布式锁的正确姿势

    一.前言 在我们日常工作中,除了Spring和Mybatis外,用到最多无外乎分布式缓存框架——Redis.但是很多工作很多年的朋友对Redis还处于一个最基础的使用和认识.所以我就像把自己对分布式缓 ...

  3. Redis全方位详解--数据类型使用场景和redis分布式锁的正确姿势

    一.Redis数据类型 1.string string是Redis的最基本数据类型,一个key对应一个value,每个value最大可存储512M.string一半用来存图片或者序列化的数据. 2.h ...

  4. 【分布式缓存系列】集群环境下Redis分布式锁的正确姿势

    一.前言 在上一篇文章中,已经介绍了基于Redis实现分布式锁的正确姿势,但是上篇文章存在一定的缺陷——它加锁只作用在一个Redis节点上,如果通过sentinel保证高可用,如果master节点由于 ...

  5. Spring Boot 2实现分布式锁——这才是实现分布式锁的正确姿势!

    参考资料 网址 Spring Boot 2实现分布式锁--这才是实现分布式锁的正确姿势! http://www.spring4all.com/article/6892

  6. Redis实现分布式锁的正确姿势

    分布式锁一般有三种实现方式:1. 数据库乐观锁:2. 基于Redis的分布式锁:3. 基于ZooKeeper的分布式锁.本篇博客将介绍第二种方式,基于Redis实现分布式锁.虽然网上已经有各种介绍Re ...

  7. Redis分布式锁的正确姿势

    1. 核心代码: import redis.clients.jedis.Jedis; import java.util.Collections; /** * @Author: qijigui * @C ...

  8. 掌握Redis分布式锁的正确姿势

    本文中案例都会在上传到git上,请放心浏览 git地址:https://github.com/muxiaonong/Spring-Cloud/tree/master/order-lock 本文会使用到 ...

  9. Redis: 分布式锁的正确实现方式(转)

    前言 分布式锁一般有三种实现方式:1. 数据库乐观锁:2. 基于Redis的分布式锁:3. 基于ZooKeeper的分布式锁.本篇博客将介绍第二种方式,基于Redis实现分布式锁.虽然网上已经有各种介 ...

随机推荐

  1. ESA2GJK1DH1K基础篇: Android连接MQTT简单的Demo

    题外话 我老爸也问我物联网发展的趋势是什么!!!!!! 我自己感觉的:(正在朝着 "我,机器人" 这部电影的服务器方向发展) 以后的设备都会和服务器交互,就是说本地不再做处理,全部 ...

  2. 6-STM32物联网开发WIFI(ESP8266)+GPRS(Air202)系统方案安全篇(Wi-Fi模块SSL连接MQTT)

    5-STM32物联网开发WIFI(ESP8266)+GPRS(Air202)系统方案安全篇(配置MQTT的SSL证书,验证安全通信) 首先确定自己的固件打开了SSL,升级篇里面的固件我打开了SSL,如 ...

  3. 【JZOJ5738】【20190706】锁屏杀

    题目 $n \le 2000 $ 题解 \(B\)的数字可以对1440取模,对三个图分别进行\(dp\)即可 时间复杂度\(O(n\times 1440 \times 10)\) Code #incl ...

  4. 如何实现有返回值的多线程 JAVA多线程实现的三种方式

    可返回值的任务必须实现Callable接口,类似的,无返回值的任务必须Runnable接口.执行Callable任务后,可以获取一个Future的对象,在该对象上调用get就可以获取到Callable ...

  5. Linux内核中的IS_ERR()实现

    1.前言 对于任何一个指针来说,必然有三种情况:一种是有效指针,一种是NULL,也就是空指针,一种是错误指针,也就是无效指针,在Linux内核中,所谓的错误指针就是指其已经到达了内核空间的最后一个pa ...

  6. Python内网渗透扫描器Ladon

    Ladon Scanner For Python PyLadon 目前python版功能较少,无论在Windows还是Linux系统性能以及速度均也比不上Ladon.exe 唯一的优点是跨平台,后续会 ...

  7. SQL语句替换某个字段的部分数据

    update `表名` set `字段`= replace(字段, '旧数据', '新数据');

  8. Intellij IDEA 中的 Debug 控制台输出窗口不见了的解决办法

    在 Debug 工具窗口,如图点击左侧重置布局图标,你的console窗口就还原了.

  9. count和distinct

    一.count和distinct count是统计数据条数,distinct是去掉重复列: count统计的时候会忽略null值,distinct会将重复的null值列作为一个. 综上select c ...

  10. Git系列 —— github提交一个空目录

    转载自github提交一个空目录 github默认不上传空目录,有的时候需要空目录来保持程序的结构. 两种场景 1.始终保持空目录,即时里面有文件,也全部忽略掉. 建立一个.gitignore文件放到 ...