目录

  • 锁实现的注意点
  • 加锁
    • connect 与 pconnect
  • 解锁
  • Redis 中使用 Lua 脚本的注意点
  • Redis集群分布式锁
    • RedLock 算法

锁实现的注意点

  1. 互斥: 任意时刻, 只能有一个客户端获得锁
  2. 不会死锁: 客户端持有锁期间崩溃, 没有主动解除锁, 能保证后续的其他客户端获得锁
  3. 锁归属标识: 加锁和解锁的必须是同一个客户端, 客户端不能解掉非自己持有的锁(锁应具备标识)

如果是Redis集群, 还得考虑具有容错性: 只要大部分Redis节点正常运行, 客户端就可以加锁和解锁.

以下只考虑 Redis单机部署的 场景.

如果是Redis集群部署, 可以使用

加锁

php 加锁示例

$redis = new Redis();
$redis->pconnect("127.0.0.1", 6379);
$redis->auth("password");   // 密码验证
$redis->select(1);  // 选择所使用的数据库, 默认有16个$key = "...";
$value = "...";
$expire = 3;// 参数解释 ↓
// $value 加锁的客户端请求标识, 必须保证在所有获取锁清秋的客户端里保持唯一, 满足上面的第3个条件: 加锁/解锁的是同一客户端
// "NX" 仅在key不存在时加锁, 满足条件1: 互斥型
// "EX" 设置锁过期时间, 满足条件2: 避免死锁
$redis->set($key, $value, ["NX", "EX" => $expire])

执行上面代码结果:

  1. $key 对应的锁不存在, 进行加锁操作
  2. $key 对应的锁已存在, 什么也不做

加锁容易错误的点:

  • 使用 setnxexpire 的组合

    原因: 若在 setnx 后脚本崩溃会导致死锁

$value 客户端标识的:

  • 简单点就用 毫秒级unix时间戳 + 客户端标识(大部分情况下够用了)
  • 使用其他算法确保生成唯一随机值

connect 与 pconnect

在php中, 若使用 pconnect 连接redis, 则在当前脚本声明周期结束后, 与redis建立的连接仍会保留, 直到对应fpm进程的生命周期结束, 同时在下一次请求时, fpm会重用该连接.

即该连接的生命周期是 fpm 进程的生命周期, 而非一次php脚本的执行.

若代码使用 pconnect, close 的作用仅是使当前php脚本不能再进行redis请求, 并没有真正关闭与redis的连接, 连接在后续请求中仍然会被重用.

pconnect函数在线程版本中不能被使用

解锁

php解锁示例: 使用lua脚本

$key = "...";
$identification = "...";
// KEYS 和 ARGV 是lua脚本中的全局变量
$script = <<< EOF
if redis.call("get", KEYS[1]) == ARGV[1] thenreturn redis.call("del", KEYS[1])
thenreturn 0
end
EOF;
# $result = $redis->eval($script, [$key, $identification], 1);
// 返回结果 >0 表示解锁成功
// php中参数的传递顺序与标准不一样, 注意区分
// 第2个参数表示传入的 KEYS 和 ARGV, 通过第3个参数来区分, KEYS 在前, ARGV 在后
// 第3个参数表示传入的 KEYS 的个数
$result = $redis->evaluate($script, [$key, $identification], 1);    

使用Lua脚本的原因:

  • 避免误删其他客户端加的锁

    eg. 某个客户端获取锁后做其他操作过久导致锁被自动释放, 这时候要避免这个客户端删除已经被其他客户端获取的锁, 这就用到了锁的标识.

  • lua 脚本中执行 getdel 是原子性的, 整个lua脚本会被当做一条命令来执行

  • 即使 get 后锁刚好过期, 此时也不会被其他客户端加锁

eval命令执行Lua代码的时候,Lua代码将被当成一个命令去执行,并且直到eval命令执行完成,Redis才会执行其他命令。

由于 script 执行的原子性, 所以不要在script中执行过长开销的程序,否则会验证影响其它请求的执行。

解锁容易错误的点:

  • 直接 del 删除键

    原因: 可能移除掉其他客户端加的锁(在自己的锁已过期情况下)

  • get判断锁归属, 若符合再 del

    原因: 非原子性操作, 若在 get 后锁过期了, 此时别的客户端进行加锁操作, 这里的 del 就会错误的将其他客户端加的锁解开.

Redis 中使用 Lua 脚本的注意点

↓ 这一段内容转载自 https://blog.csdn.net/zhouzme/article/details/53046606

注意点:

  1. Redis 会把所有执行过的脚本都缓存在内存中

  2. Redis 在重启的时候会释放掉之前保存的脚本

  3. Lua 脚本中所需要用到的键名以及参数一定要使用 KEYS 和 ARGV 来替换,千万不要写死在代码中,除非你百分百确定每次请求时他们是固定不变的值,特别是涉及到 时间,随机数的,一定要用参数代入,因为 Redis 每次使用 script 都会校验脚本缓存中是否已存在相同脚本,否则就会存储到缓存中,如果你的脚本很长,且每次请求存在不同的变量值,则会生成无数多个脚本缓存,你将会发现Redis占用的内存会唰唰唰的往上涨,我一开始因为key 和 参数太多,分开写太麻烦了,就图省事方便,直接把变量拼接到脚本里面,结果发现内存不停的涨,很是抓狂,找了好久才发现是这么个原因。

  4. Lua 中脚本定义变量一定要使用局部变量, 即 local var = 1, 局部变量只在所定义的块(指控制结构, 函数或chunk等)内有效, 使用局部变量可以避免命名冲突 并且访问更快(lua中局部变量和全局变量存储方式是不一样的)

  5. 如果Lua脚本写的比较长,非本地或局域网的情况下,建议使用 SHA 签名的方法来调用,这样节省带宽,但对性能似乎没什么直接的提升。这里对小白普及下我理解的原理就是 Redis 会把每个脚本都生成唯一签名,把脚本作为函数体,并使用该签名作为脚本的函数名放到缓存中,所以后面调用就只需要传一个 SHA 签名就可以调用该函数了,精简很多了。同一个脚本生成的签名都是相同的,所以SHA签名可以先在本地生成,然后在服务器上 script load 一次脚本,程序中只需保存和使用该签名即可。另外需要注意的是,脚本如果被改动哪怕一个换行或一个空格(这些容易被忽略或误操作)都必须重新 load 来获取新的 SHA

    注意:获取 SHA 签名是单独的功能,不要放在你的正常流程中,当本地开发时就可以生成SHA,把字符串写死在流程中。同样的脚本,Reids是始终生成相同的签名的。

  6. 通过 eval 带入的 ARGV 参数如果原来是数字的,会被转换为字符串,如果你的逻辑中需要判断该变量 > 0 或 < 0 之类的数字判断则必须进行字符串到数字的转换,使用 tonumber() 方法if (tonumber(ARGV[1]) > 0) then return 1; end;

  7. 我测试了几个 lua script 与 PIPELINE 处理对比,发现 script 的效率一般比 PIPELINE 高 30% ~ 40% 左右

Redis集群分布式锁

Redis 集群相对单机来说, 需要考虑一个 容错性, 设计上更为复杂

由于这个我也从未实践过, 先贴一个官方的教程贴压压惊

https://github.com/antirez/redis-doc/blob/master/topics/distlock.md

对应的翻译: http://ifeve.com/redis-lock/

RedLock 算法

官方给出了一个 RedLock 算法

情景: 当前有N个完全独立的Redis master节点, 分别部署在不同的主机上

客户端获取锁的操作:

  1. 使用相同key和唯一值(作为value)同时向这N个redis节点请求锁, 锁的超时时间应该 >> 超时时间(考虑到请求耗时), 若某个节点阻塞了了应尽快跳过
  2. 计算步骤1消耗的时间, 若总消耗时间超过超时时间, 则认为锁失败. 客户端需在大多数(超过一半)的节点上成功获取锁, 才认为是锁成功.
  3. 如果锁成功了, 则该锁有效时间就是 锁原始有效时间 - 步骤1消耗的时间
  4. 如果锁失败了(超时或无法获取超过一半 N/2 + 1 实例的锁), 客户端会到每个节点释放锁(是每个, 即使之前认为加锁失败的节点)

转载于:https://www.cnblogs.com/youjiaxing/p/10437674.html

[原创] PHP 使用Redis实现锁相关推荐

  1. 死磕 java同步系列之redis分布式锁进化史

    问题 (1)redis如何实现分布式锁? (2)redis分布式锁有哪些优点? (3)redis分布式锁有哪些缺点? (4)redis实现分布式锁有没有现成的轮子可以使用? 简介 Redis(全称:R ...

  2. java redis的同步_java同步系列之redis分布式锁进化史

    标题: 死磕 java同步系列之redis分布式锁进化史 - 彤哥读源码 - 博客园 转帖原地址: https://www.cnblogs.com/tong-yuan/p/11621361.html ...

  3. Redlock:Redis分布式锁最牛逼的实现

    普通实现 说道Redis分布式锁大部分人都会想到:setnx+lua,或者知道set key value px milliseconds nx.后一种方式的核心实现命令如下: - 获取锁(unique ...

  4. 集群部署中解决定时任务重复执行的问题-redis分布式锁应用

    背景描述 有小伙伴私信我,关于存在定时任务的项目在集群环境下部署如何解决重复执行的问题,PS:定时任务没有单独拆分. 概述:之前的项目都是单机器部署,所以定时任务不会重复消费,只会执行一次.而在集群环 ...

  5. Redis分布式锁的正确打开方式

    Redis分布式锁的正确打开方式 为什么需要分布式锁 分布式锁原理 单机锁和分布式锁的联系与区别 Redis分布式锁的演进史 第一版 SETNX 如何避免死锁 解决锁被别人释放 锁过期时间不好评估 R ...

  6. redis分布式锁 在集群模式下如何实现_收藏慢慢看系列:简洁实用的Redis分布式锁用法...

    在微服务中很多情况下需要使用到分布式锁功能,而目前比较常见的方案是通过Redis来实现分布式锁,网上关于分布式锁的实现方式有很多,早期主要是基于Redisson等客户端,但在Spring Boot2. ...

  7. 快来学习Redis 分布式锁的背后原理

    以前在学校做小项目的时候,用到Redis,基本也只是用来当作缓存.可阿粉在工作中发现,Redis在生产中并不只是当作缓存这么简单.在阿粉接触到的项目中,Redis起到了一个分布式锁的作用,具体情况是这 ...

  8. Redis分布式锁使用不当,酿成一个重大事故,超卖了100瓶飞天茅台!!!

    点击关注公众号,Java干货及时送达 来源:juejin.cn/post/6854573212831842311 基于Redis使用分布式锁在当今已经不是什么新鲜事了. 本篇文章主要是基于我们实际项目 ...

  9. Redis 分布式锁使用不当,酿成一个重大事故,超卖了100瓶飞天茅台!!!

    点击上方蓝色"方志朋",选择"设为星标" 回复"666"获取独家整理的学习资料! 基于Redis使用分布式锁在当今已经不是什么新鲜事了. 本 ...

最新文章

  1. linux编译安装jpeg,Linux下JPEG库安装脚本(转)
  2. 文件描述符在内核态下的一些小把戏
  3. nginx 上传 文件超时设置_Nginx大文件上传413和500问题排查总结
  4. 手把手教你如何写简历
  5. 使用SQL Server分区表功能提高数据库的读写性能
  6. IDEA 将 SpringBoot 项目打包成jar
  7. MPLS virtual private network OptionA实验(华为设备)
  8. Jasmine基础API
  9. laravel-echo-server 不接收失败_6所高校公布报名不合格名单!这些问题最容易出错...
  10. 小巫随笔12(致小巫逝去的童年),2021最新阿里Android面试流程
  11. 3D Food Printing【3D食物打印】
  12. 修复损坏图片的c语言,如何自助修复损坏的JPEG照片和图像,文末有好方法~
  13. HECTF2021-WP集合
  14. nltk中文分句_如何改进NLTK的分句技术?
  15. 一款语文老师写的word办公神级插件
  16. 航班时间(第九届蓝桥杯省赛C++A组)
  17. Git, GitHub使用记录
  18. TensorFlow2学习七、使用MNIST手写体识别数据集识别自己手写图片
  19. 光纤布线系统的设计与检测(二)
  20. 三星android获取root权限,三星S9 G9600 9.0 root教程_获取安卓9.0系统的root权限的方法...

热门文章

  1. CentOS 7 更换 yum 源
  2. 从头开始搭建爬虫环境
  3. “云+大数据”时代 中端存储如何选择
  4. 从扁平到立体:Windows 10 图标的演化
  5. 「服务端」node服务的监控预警系统架构
  6. 我更喜欢用 Intellij IDEA 部署应用
  7. linux的用户管理与权限学习总结
  8. Win7无线网络共享设置方法
  9. Struts2中的ActionContext
  10. 几篇关于Hadoop+Hive数据仓库的入门文章