laravel中锁以及事务的简单使用
一、首先来说一下什么是共享锁?什么是排他锁?
共享:我可以读
写
加锁
, 别人可以 读
加锁
。
排他:只有我 才 可以 读
写
加锁
, 也就是说,必须要等我提交事务,其他的才可以操作。
二、简单例子实现加锁
锁和事务在使用时需要配合使用,也就是用锁时需要先开启事务,事务提交时,会自动解锁。
DB::beginTransaction(); // 开启事务$good = \App\Models\Good::sharedLock()->first(); //共享锁 s锁 读锁
// $good = \App\Models\Good::lockForUpdate()->first(); //排他锁 x锁 写锁...DB::commit();
DB::beginTransaction();
$goodsInfo = Goods::where('goods_id',$gid)->lockForUpdate()->first();
$goodsInfo->seckill_stock-=1;
$goodsInfo->save();
DB::commit();
三、怎样利用锁和事务解决并发问题?
在我们的工作中,常常会出现一些对数量控制有精确要求的需求,比如商品库存量、奖品数量、报名人数限制等等,这些应用场景往往都存在高并发可能,比较容易出现数据量超量问题。以下做一下示例探索:
(1)首先设计一个存量表
CREATE TABLE `product` (`id` int(11) NOT NULL AUTO_INCREMENT,`product_name` varchar(255) NOT NULL DEFAULT '',`count` int(10) NOT NULL DEFAULT '0',PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8mb4;
(2)添加一行数据如下,设定基础库存量为 10
(3)问题代码如下:
$process_num = 50; //开50个进程,模拟50个用户for ($i = 0; $i < $process_num; $i++) {MultiProcessHelper::instance($process_num)->multiProcessTask(function () use ($i) {if (Db::name('product')->where('id', 1)->value('count') > 0) {$res = Db::name('product')->where('id', 1)->setDec('count');if ($res) {dump('获取到更新资源权限:' . $i);}}});}
执行结果,50 个用户都获取到了更新资源的权限,但是数据库相应数据存量变成了 - 40
高并发带来的问题,同一时刻有多个进程读取同一条数据,同一时刻有多个进程更新同一条数据
(4)解决方案
1.方案1
要进行 DML 层面的限制(最后关卡安全,报错总比出现数据问题产生的影响小),主要的修改是将 count 的类型改成了无符号整数,这样该值就不可能再出现负数值
CREATE TABLE `product` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`product_name` varchar(255) NOT NULL DEFAULT '',
`count` int(10) unsigned NOT NULL DEFAULT '0',
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8mb4;
执行一下代码,当 count 值从 10 减到 0 时,就不能再减少了,再减就会出现数据库报错
2.方案2
mysql 提供的行级锁 select ... lock in share mode(阻塞写),select ... for update(阻塞读写,悲观锁),所以 for update 机制能满足我们的原子要求。编辑代码如下:
$process_num = 50; //开50个进程,模拟50个用户for ($i = 0; $i < $process_num; $i++) {MultiProcessHelper::instance($process_num)->multiProcessTask(function () use ($i) {Db::startTrans(); //行级锁必须在事务中才能生效//设置for update,进程会阻塞在这里,只能允许一个进程获取到行锁,其他等待获取if (Db::name('product')->where('id', 1)->lock('for update')->value('count') > 0) { $res = Db::name('product')->where('id', 1)->setDec('count');if ($res) {dump('获取到更新资源权限:' . $i);}}Db::commit();});}
只有十个进程获取到了更新权限,消费正常
3.方案3
将条件语句放到 update 上,保持语句执行的原子性,杜绝并发幻读
修改代码如下:
$process_num = 50; //开50个进程,模拟50个用户for ($i = 0; $i < $process_num; $i++) {MultiProcessHelper::instance($process_num)->multiProcessTask(function () use ($i) {//合并两条语句为一条更新语句$res = Db::name('product')->where('id', 1)->where('count', '>', 0)->setDec('count');if ($res) {dump('获取到更新资源权限:' . $i);}});}
只有十个进程获取到了更新权限,消费正常
4.方案4
文件锁机制解决
$process_num = 50; //开50个进程,模拟50个用户for ($i = 0; $i < $process_num; $i++) {MultiProcessHelper::instance($process_num)->multiProcessTask(function () use ($i) {$filename = app()->getRootPath() . 'runtime/lock';$file = fopen($filename, 'w'); //打开文件$lock = flock($file, LOCK_EX);// $lock=flock($handle, LOCK_EX|LOCK_NB); (异步非阻塞,所有进程如果出现获取不到锁,不等待跳过,加锁失败)//获取文件排他锁:LOCK_EX(异步阻塞,只有一个进程获得锁,其他竞争进程等待)//还有一种共享锁:LOCK_SH(所有进程都可以获取共享锁,读取文件,当且只有一个锁时,才允许写操作,否则操作失败,容易出现死锁问题)if ($lock) {try {if (Db::name('product')->where('id', 1)->lock('for update')->value('count') > 0) {$res = Db::name('product')->where('id', 1)->setDec('count');if ($res) {dump('获取到更新资源权限:' . $i);}}} catch (\Exception $e) {dump($e->getMessage());} finally {flock($file, LOCK_UN); //无论如何都要释放锁}}fclose($file); //关闭文件句柄});}
只有十个进程获取到了更新权限,消费正常
5.方案5
分布式锁机制解决
以上文件锁,只适应于单体架构的需求,在集群架构、分布式等多机联网结构中就是掩耳盗铃了,所以适应性更好地锁机制还是要使用分布式锁,分布式锁最常用和最易用就是 redis 的 setnx 锁了。
$process_num = 50; //开50个进程,模拟50个用户for ($i = 0; $i < $process_num; $i++) {MultiProcessHelper::instance($process_num)->multiProcessTask(function () use ($i) {//获取redis锁//关于CacheHelper::getRedisLock是怎样获取锁的,注意几个点就行:1.如何避免死锁;2.如何设置过期时间;3.如何设置抢占条件;4.如何循环等待判断。这些不在本文讨论范围,可自行研究,以后有空我也可以写一篇博文$lock = CacheHelper::getRedisLock('redis_lock');if ($lock) {try {if (Db::name('product')->where('id', 1)->lock('for update')->value('count') > 0) {$res = Db::name('product')->where('id', 1)->setDec('count');if ($res) {dump('获取到更新资源权限:' . $i);}}} catch (\Exception $e) {dump($e->getMessage());}} else {
// dump('获取redis锁失败');}});}
参考链接:浅谈并发加锁 | Laravel China 社区 (learnku.com)
相关文章:

laravel中锁以及事务的简单使用
一、首先来说一下什么是共享锁?什么是排他锁? 共享:我可以读 写 加锁 , 别人可以 读 加锁。 排他:只有我 才 可以 读 写 加锁 , 也就是说,必须要等我提交事务,其他的才可以操作。 二、简单例子实现加锁 锁…...

Vue+openlayers+projs4实现坐标转换
一、背景 有一堆点数据,需要在地图上标记,只知参考北京54坐标系或西安80坐标系,但具体是哪种不清楚,这时候就需要坐标转换。ps:EPSG:3857(openlayers参照的坐标系) 二、思路 1、研…...

09 创建型模式-建造者模式
1.建造者模式介绍: 建造者模式 (builder pattern), 也被称为生成器模式 , 是一种创建型设计模式 定义: 将一个复杂对象的构建与表示分离,使得同样的构建过程可以创建不 同的表示。 2.建造者模式要解决的问题 建造者模式可以将部件和其组装过程分开&am…...

4.9 多协议标记交换MPLS
思维导图: 前言: **4.9 多协议标记交换MPLS笔记** 1. **定义与背景**: - MPLS (多协议标记交换) 是一种由 IETF 开发的新协议。 - “多协议”意味着 MPLS 的上层可以使用多种协议。 - 该协议综合了多家公司的技术,如 C…...

【经历】在职8个月->丰富且珍贵
在职8个月->丰富且珍贵 2021-3~2021-11:面试进入一家做400电话的公司,我进入公司时,加上我只有四个人(老板、人事、业务),开发只有我,所以:产品~设计~前端~后端~测试~上线~维护~培训,只有我自…...
使用GH(命令行)在本地提出Github上的issue、PR,合并PR
使用GH(命令行)在本地提出Github上的issue、PR,合并PR 前言 Github上的一些操作使用Git命令是无法完成的,因此正常流程就是在网页端进行。等一下,你让程序员用网页进行?果然,有命令行工具可以…...
@Scheduled定时器
Scheduled定时器 一、基本使用二、参数说明fixedDelayfixedRateinitialDelaycron 三、cron 表达式参数说明实用性的案例 四、Scheduled注意事项五、配置文件1、fixedDelay2、fixedRate3、cron 一、基本使用 Scheduled // 由Spring定义,用于将方法设置为调度任务。…...

Xshell+screen解决ssh连接 服务器掉线的问题
Linux screen命令解决SSH远程服务器训练代码断开连接后运行中断_linux screen ssh-CSDN博客 Linux命令之screen命令_linux screen_恒悦sunsite的博客-CSDN博客 使用教程: 这里粗略介绍一下 (1)xshell xftp(xshell点这个&#…...
coding_v3
面试经典 150 题 - 学习计划 - 力扣(LeetCode)全球极客挚爱的技术成长平台 数组/字符串 1.LC88【合并两个有序数组】 def solve(nums1, m, nums2, n):p1, p2 m-1, n-1tail m n -1while p1 > 0 or p2 > 0:if p1 -1:nums1[tail] nums2[p2]p2…...

Elasticsearch:什么是余弦相似度?
余弦相似度是数据科学、文本分析和机器学习领域的基本概念。 如果你想知道什么是余弦相似度或者它如何在现实世界的应用程序中使用,那么你来对地方了。 本指南旨在让你深入了解相似性是什么、其数学基础、优点及其在不同领域的各种应用。读完本指南后,你…...
【每日一题Day352】LC1726同积元组 | 哈希表+排列组合
同积元组【LC1726】 给你一个由 不同 正整数组成的数组 nums ,请你返回满足 a * b c * d 的元组 (a, b, c, d) 的数量。其中 a、b、c 和 d 都是 nums 中的元素,且 a ! b ! c ! d 。 思路 求出所有二元组的积及其出现次数,假设某个积出现的次…...
react中在js文件里定义的变量,如何在less文件里去使用该变量
在 React 中,如果在 JS 文件中定义了变量,可以使用 CSS Modules 的方式将变量导出到 LESS 文件中。 以下是示例代码: // index.js import React from react; import styles from ./styles.module.less;const color red;function App() {re…...

TIA博途中通过SCATTER指令实现将字节BYTE拆分成单个位的具体方法示例
TIA博途中通过SCATTER指令实现将字节BYTE拆分成单个位的具体方法示例 例如: 我们想判断某个字节中各个位的状态是1还是0 ,如何实现呢? 这里介绍通过SCATTER指令拆分字节的方法,仅供大家参考。 首先,我们先了解以下SCATTER指令的基本功能和使用方法: 如下图所示,在基本指…...

网络协议--ARP:地址解析协议
4.1 引言 本章我们要讨论的问题是只对TCP/IP协议簇有意义的IP地址。数据链路如以太网或令牌环网都有自己的寻址机制(常常为48 bit地址),这是使用数据链路的任何网络层都必须遵从的。一个网络如以太网可以同时被不同的网络层使用。例如&#…...
pdf导出实例(itestpdf)
依赖 <!-- https://mvnrepository.com/artifact/com.itextpdf/itextpdf --><dependency><groupId>com.itextpdf</groupId><artifactId>itextpdf</artifactId><version>5.5.10</version></dependency>工具类 package or…...

关于CW32单片机pack包安装 KEIL IAR
CW32 系列微控制器软件开发工具入门 芯片包 1. 下载芯片包 官方下载链接:武汉鑫源半导体 2. 安装芯片包 双击芯片包.pack文件 支持 CW32F 系列的 IDE 支持 CW32F 系列的工具链: • • EWARM v7.70 或更高版本 MDK-ARM v5.17 或更高版本 2.1 EW…...

memcpy内存拷贝函数
目录 一、memcpy内存拷贝函数 注意事项 二、memcpy与strcpy对比 三、模拟实现memcpy函数 四、memcpy函数不能进行两块存在内存重叠的空间的内存拷贝 五、改进my_memcpy函数 一、memcpy内存拷贝函数 头文件:string.h 函数原型:void* memcpy(void* …...
数组模拟堆
文章目录 QuestionIdeasCode Question 维护一个集合,初始时集合为空,支持如下几种操作: I x,插入一个数 x ; PM,输出当前集合中的最小值; DM,删除当前集合中的最小值(数…...

【深度学习基础知识(一):卷积神经网络CNN基础知识】
深度学习基础知识 深度学习基础知识(一):卷积神经网络CNN基础知识 卷积神经网络CNN基础知识 0、目录 1. CNN卷积神经网络的特点 2. 卷积操作基础知识 2.1 卷积操作的概念2.2 卷积操作的种类2.3 卷积操作后特征图谱大小计算公式 3. 池化操…...

Git使用入门
一、Git简介 Git 是一个开源的分布式版本控制系统。 Git版本控制的功能为保存不同版本的代码,保存代码的地方叫做仓库。 每个仓库中有多个分支,每个分支上又有很多节点,每个节点代表一个版本,不同的分支可以进行合并࿰…...

IDEA运行Tomcat出现乱码问题解决汇总
最近正值期末周,有很多同学在写期末Java web作业时,运行tomcat出现乱码问题,经过多次解决与研究,我做了如下整理: 原因: IDEA本身编码与tomcat的编码与Windows编码不同导致,Windows 系统控制台…...
【网络】每天掌握一个Linux命令 - iftop
在Linux系统中,iftop是网络管理的得力助手,能实时监控网络流量、连接情况等,帮助排查网络异常。接下来从多方面详细介绍它。 目录 【网络】每天掌握一个Linux命令 - iftop工具概述安装方式核心功能基础用法进阶操作实战案例面试题场景生产场景…...

使用VSCode开发Django指南
使用VSCode开发Django指南 一、概述 Django 是一个高级 Python 框架,专为快速、安全和可扩展的 Web 开发而设计。Django 包含对 URL 路由、页面模板和数据处理的丰富支持。 本文将创建一个简单的 Django 应用,其中包含三个使用通用基本模板的页面。在此…...
ES6从入门到精通:前言
ES6简介 ES6(ECMAScript 2015)是JavaScript语言的重大更新,引入了许多新特性,包括语法糖、新数据类型、模块化支持等,显著提升了开发效率和代码可维护性。 核心知识点概览 变量声明 let 和 const 取代 var…...

突破不可导策略的训练难题:零阶优化与强化学习的深度嵌合
强化学习(Reinforcement Learning, RL)是工业领域智能控制的重要方法。它的基本原理是将最优控制问题建模为马尔可夫决策过程,然后使用强化学习的Actor-Critic机制(中文译作“知行互动”机制),逐步迭代求解…...
多场景 OkHttpClient 管理器 - Android 网络通信解决方案
下面是一个完整的 Android 实现,展示如何创建和管理多个 OkHttpClient 实例,分别用于长连接、普通 HTTP 请求和文件下载场景。 <?xml version"1.0" encoding"utf-8"?> <LinearLayout xmlns:android"http://schemas…...
【解密LSTM、GRU如何解决传统RNN梯度消失问题】
解密LSTM与GRU:如何让RNN变得更聪明? 在深度学习的世界里,循环神经网络(RNN)以其卓越的序列数据处理能力广泛应用于自然语言处理、时间序列预测等领域。然而,传统RNN存在的一个严重问题——梯度消失&#…...

Redis数据倾斜问题解决
Redis 数据倾斜问题解析与解决方案 什么是 Redis 数据倾斜 Redis 数据倾斜指的是在 Redis 集群中,部分节点存储的数据量或访问量远高于其他节点,导致这些节点负载过高,影响整体性能。 数据倾斜的主要表现 部分节点内存使用率远高于其他节…...
服务器--宝塔命令
一、宝塔面板安装命令 ⚠️ 必须使用 root 用户 或 sudo 权限执行! sudo su - 1. CentOS 系统: yum install -y wget && wget -O install.sh http://download.bt.cn/install/install_6.0.sh && sh install.sh2. Ubuntu / Debian 系统…...

网站指纹识别
网站指纹识别 网站的最基本组成:服务器(操作系统)、中间件(web容器)、脚本语言、数据厍 为什么要了解这些?举个例子:发现了一个文件读取漏洞,我们需要读/etc/passwd,如…...