PHP+redis 优雅实现加锁机制

setNX,是set if not exists 的缩写,也就是只有不存在的时候才设置, 设置成功时返回 1 , 设置失败时返回 0 。可以利用它来实现锁的效果,但是很多人在使用的过程中都有一些问题没有考虑到。

例如某个查询数据库的接口因为请求量比较大所以加了缓存,并设定缓存过期后刷新。当并发量比较大并且缓存过期的瞬间,大量并发请求会直接查询数据库导致雪崩。如果使用锁机制来控制只有一个请求去更新缓存就能避免雪崩的问题。下面是很多人下意识想到的加锁方法

$rs = $redis->setNX($key, $value);
if ($rs) {//处理更新缓存逻辑// ......//删除锁$redis->del($key);
}

通过 setNX 获取锁,如果成功了则更新缓存然后删除锁。其实这里有一个严重的问题:如果更新缓存的时候因为某些原因意外退出了,那么这个锁就不会被删除而一直存在,以至于缓存再也得不到更新。为了解决这个问题有人可能会想到给锁设置一个过期时间,如下

$redis->multi();
$redis->setNX($key, $value);
$redis->expire($key, $ttl);
$redis->exec();

因为 setNX 不具备设置过期时间的功能,所以要借助 Expire 来设置,同时需要使用 Multi/Exec 来确保请求的原子性,以免 setNX 成功了 Expire 却失败了。这样还有问题:当多个请求到达时,虽然只有一个请求的 setNX 可以成功,但是任何一个请求的 Expire 却都可以成功,这就意味着即便获取不到锁也可以刷新过期时间,导致锁一直有效,还是解决不了上面的问题。显然 setNX 满足不了需求,Redis从 2.6.12 起,SET 涵盖了 SETEX 的功能, SET 本身又包含了设置过期时间的功能,所以使用 SET 就可以解决上面遇到的问题

$rs = $redis->set($key, $value, array('nx', 'ex' => $ttl));
if ($rs) {//处理更新缓存逻辑// ......//删除锁$redis->del($key);
}

到这一步其实还是有问题的,如果一个请求更新缓存的时间比锁的有效期还要长,导致在缓存更新过程中锁就失效了,此时另一个请求就会获取到锁,但前一个请求在缓存更新完毕的时候,直接删除锁的话就会出现误删其它请求创建的锁的情况。所以要避免这种问题,可以在创建锁的时候需要引入一个随机值并在删除锁的时候加以判断

$rs = $redis->set($key, $random, array('nx', 'ex' => $ttl));
if ($rs) {//处理更新缓存逻辑// ......//先判断随机数,是同一个则删除锁if ($redis->get($key) == $random) {$redis->del($key);}
}

redis的setnx可以加锁、实际使用中用不好很容易会发生各种问题造成死锁

分享一下核心的稳定的redis之nx代码、php使用setnx很容易因为不严谨造成死锁,分享一下正确写法
		$redisKey = 'key:member_id:'.$member_id;if($redis->setnx($redisKey,time())){//此处使用setnx(key,1)会有原子性问题$redis->expire($redisKey,5);//设置过期时间、其实会有原子性问题goto unlock;}else{//防止死锁if($nx_lock = $redis->get($redisKey)) {//获取锁的值$dif_time = time() - $nx_lock;//判断锁是否过期、防止死锁、因为(设置过期时间、其实会有原子性问题)if($dif_time > 5){//防止死锁$redis->del($redisKey);//释放死锁}return $this->error("系统繁忙,请稍后再试!");}}unlock:#todo解锁代码

 

 public static function setnx(string $key, string $data = '', int $expire = null){if (empty($key)) {return false;}self::init();if($expire){$res = self::$redis->set($key, $data, ['nx', 'ex' => $expire]);}else{$res = self::$redis->setnx($key,$data);}// 防止死锁if(!$res && $expire){$ttl = self::$redis->ttl($key);if($ttl === -1 || $ttl > $expire){self::$redis->expire($key, $expire);}}return $res;}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/31771.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

DeepSeek-实用集成大礼包

随着DeepSeek的持续火热,在各种平台看到大家基于deepseek+各类应用的案例。这些案例真假难辨,现在DeepSeek已经推出了官方的Awesome DeepSeek Integrations,集成了各类应用,下面是详细的介绍。 DeepSeek Integrations 是 DeepSeek 官方在 GitHub 上精心整理的一个集合了各种…

Ubuntu安装问题汇总

参考文章: 【Ubuntu常用快捷键总结】 【王道Python常用软件安装指引】 1. 无法连接虚拟设备 sat0:0 【问题】:出现下图所示弹框。 【问题解决】: 点击 “否” 。 点击左上角的 “虚拟机” → “设置…” → “CD/DVD (SATA)” ,…

深陷帕金森困境,怎样重燃生活信心?

帕金森,这个悄然影响无数中老年人生活的神经系统疾病,正逐渐走进大众视野。患病后,患者常出现静止性震颤,安静时手部、下肢不自主抖动,如同在默默诉说着身体的异常。肢体变得僵硬,行动迟缓,起步…

空间遥感智能处理技术发展现状与趋势

在数字化时代,空间遥感技术已经成为获取地球表面信息的重要手段。随着卫星遥感技术的快速发展,获取的遥感数据量激增,这对遥感数据的智能处理提出了更高的要求。本文将探讨空间遥感智能处理技术的发展现状与未来趋势。 发展现状 大数据与人工…

svn删除所有隐藏.svn文件,文件夹脱离svn控制

新建一个文件,取名remove-svn-folders.reg,输入如下内容: Windows Registry Editor Version 5.00 [HKEY_LOCAL_MACHINE\SOFTWARE\Classes\Folder\shell\DeleteSVN] "Delete SVN Folders" [HKEY_LOCAL_MACHINE\SOFTWARE\Class…

Datawhale coze-ai-assistant 笔记2

目录 快速搭建一个 AI 助手智能体 搭建步骤 步骤1:创建一个智能体 步骤2:编写提示词 步骤3:调试智能体 步骤4:发布智能体 设置智能体模型 选择模型 生成多样性 输入及输出设置 如何使用 步骤1:更换模型 步…

win10电脑鼠标速度突然变的很慢?

电脑鼠标突然变很慢,杀毒检测后没问题,鼠标设置也没变,最后发现可能是误触鼠标的“DPI”调节键。 DPI调节键在鼠标滚轮下方,再次点击即可恢复正常鼠标速度。 如果有和-的按键,速度变快,-速度变慢。 图源&…

若依RuoYi-Cloud-Plus微服务版(完整版)前后端部署

一.目标 在浏览器上成功登录进入 二.源码下载 后端源码:前往Gitee下载页面(https://gitee.com/dromara/RuoYi-Cloud-Plus)下载解压到工作目录。 前端源码: 前往Gitee下载页面(https://gitee.com/JavaLionLi/plus-ui)下载解压到工作目录。 文档地址&a…

vue3+elementuiplus的table表格动态高度

table表格流体高度 1、前提 了解自定义指令、hooks 2、核心思路 通过自定义指令(new ResizeObserver)监听表格变化,然后通过hooks去更新表格高度。 3、核心代码 src/directives/resize.ts // import { debounce } from /utils;import { t…

Django与数据库

我叫补三补四,很高兴见到大家,欢迎一起学习交流和进步 今天来讲一讲alpha策略制定后的测试问题 mysql配置 Django模型体现了面向对象的编程技术,是一种面向对象的编程语言和不兼容类型能相互转化的编程技术,这种技术也叫ORM&#…

VMware下载安装Ubuntu详解

一、Linux简介 1、不同领域的主流操作系统 桌面操作系统服务器操作系统移动设备操作系统嵌入式操作系统 1.1、桌面操作系统 Windows(用户数量最多)Mac OS(苹果电脑操作系统)Linux(用户数量少) 1.2、服…

动态规划详解(二):从暴力递归到动态规划的完整优化之路

目录 一、什么是动态规划?—— 从人类直觉到算法思维 二、暴力递归:最直观的问题分解方式 1. 示例:斐波那契数列 2. 递归树分析(以n5为例) 3. 问题暴露 三、第一次优化:记忆化搜索(Memoiza…

下降路径最⼩和(medium)

题目描述: 给你一个 n x n 的 方形 整数数组 matrix ,请你找出并返回通过 matrix 的下降路径 的 最小和 。 下降路径 可以从第一行中的任何元素开始,并从每一行中选择一个元素。在下一行选择的元素和当前行所选元素最多相隔一列&#xff08…

YashanDB认证,YCA证书认证教程,免费证书,内含真题考试题库及答案——五分钟速成

目录 一.账号及平台注册登录流程 二.登录进行设备调试核验 三.考试(考完获取分数) 四.获取证书 五.题库及答案 一.账号及平台注册登录流程 1-点击这里进行账号注册(首次学习必须先注册,有账号之后可以直接在2号链接登录&#…

texstudio: 编辑器显示行号+给PDF增加行号

texstudio在编辑器部分增加行号: texstudio默认在编辑器部分不显示行号,如下图: 要实现以下的在编辑部分增加行号: 执行如下操作: 选项-->设置TexStudio-->编辑器-->显示行号-->所有行号选择好后&…

解决vscode中出现“无法将pip项识别...“问题

问题 遇见问题如下: 查看pip 通过 winR ,输入 cmd,进入终端,搜索 where pip。 发现 pip 查不出来,然后进入文件资源管理器,搜索 Scripts 文件夹,如果没有找到可能是电脑没有下载 python。 点击…

【webrtc debug tools】 rtc_event_log_to_text

一、rtc_event_log 简介 在学习分析webrtc的过程中,发现其内部提供了一个实时数据捕获接口RtcEventLog。通过该接口可以实时捕获进出webrtc的RTP报文头数据、音视频配置参数、webrtc的探测数据等。其内容实现可参考RtcEventLogImpl类的定义。其文件所在路径 loggin…

华为eNSP:2.配置OSPF报文分析和验证

一、OSPF的5种数据包 Hello包:用于发现和维护邻居关系。定期发送,确保邻居路由器在线。 数据库描述包(DBD, Database Description Packet):在邻居关系建立后,用于交换链路状态数据库的摘要信息。 链路状…

初次体验Tauri和Sycamore(3)通道实现

​ 原创作者:庄晓立(LIIGO) 原创时间:2025年03月10日(发布时间) 原创链接:https://blog.csdn.net/liigo/article/details/146159327 版权所有,转载请注明出处。 20250310 LIIGO备注&…

DBeaver安装教程+连接TDengine数据库

为TDengine安装的DBeaver教程 安装 23.1.1 版本以上的DBeaver 因为官方文档说这个版本之上的DBeaver才支持TDengine内嵌前往DBeaver 官方文档进行版本下载滑到链接最下面点击进入 点击download,进入选择下载版本 等待下载成功即可双击自行安装 打开数据库连接TDen…