MySQL乐观锁与悲观锁

说明

遇见并发情况,需要保证数据的准确性,也就是与正确的预期一致,此时就会用到锁。
锁是在并发下控制程序的执行逻辑,以此来保证数据按照预期变动。
如果不加锁,并发情况下的可能数据不一致的情况,这是个概率问题。

乐观锁CAS

简介

乐观锁很乐观,假设数据一般情况不会造成冲突,属于程序层面的逻辑锁,在数据进行更新时,才进行锁的检测。是通过添加一个版本号的方式实现的,每当数据这一行所在的数据发生变化,则对应的版本号+1,更新数据时,将版本号作为查询条件。
至于是否要加事务,看写操作单条数据还是写操作多条数据。

注意:网上很多解决方案用时间戳来做version字段,我持反对意见,并发可能是一瞬间的事,不到一秒就有好多请求,用时间戳粒度太大,用随机字符串都比用这个强。

用法

#示例
update test set score = score + 1 where id = 1
#优化为,这种简单,但是会有ABA的问题:
select score as old_score from test where id  = 1;
update test set score = score + 1 where id = 1 and score = old_score;
#或者添加一个version字段,这种不存在ABA的问题
select version from test where id  = 1;
update test set score = score + 1 where id = 1 and version = version;

适用场景

  1. 读多写少:由于并发写操作较少,乐观锁的修改数据受影响行数为0概率也较低。
  2. 允许一定量的重试或不需要重试的场景:这个要根据业务,否则来回重试会降低性能。

优点

实现简单:乐观锁在代码上就可以实现,不需要额外对数据库额外操作。
无死锁风险:悲观锁有死锁风险,乐观锁没有。
无需重试情况下,性能较高:乐观锁机制在并发访问情况下,不需要像悲观锁那样阻塞其他事务,提供了更高的并发性能,前提当前业务需求能容忍写操作失败的情况。

缺点

并发冲突:多加了一个where条件,只能保证数据最终不会出错,不能保证每条写操作的SQL都执行成功(也就是受影响行数>0)。
不提供强一致性:强一致性要求数据的状态在任何时刻都保持一致,悲观锁是到写操作那一步才去验证,期间只是做了个where条件的过滤。
ABA问题:一个字段的值在请求X中查询出来是A,后续代码实现乐观锁,因为并发量大,同时过来一个Y请求,将A值改成了B,因为一些业务原因又改成了A,整个过程虽然不影响请求X的结果,且能正常执行,但是联合其它数据,这个情况是否符合业务场景,不好说,所以最好的解决方案,就是专门做一个version字段,且不会与之前的version重复,即可,把这个version字段作为where条件,而不是存A或者B字段的所在字段作为where条件。

悲观锁

简介

悲观锁比较悲观,假设数据一定会造成冲突,属于MySQL层面的锁。通过加锁阻塞其他事务,悲观锁可以保证在任何时刻,只有一个事务能够修改或访问共享资源,从而实现了强一致性。这意味着在悲观锁机制下,每个事务的读写操作都是有序、线性的。
需要事务的参与。

用法

在事务中的查询语句添加for update即可。

如果此时执行了三行内容没有commit,再次执行update test set score = score + 1 where id = 1;则处于阻塞状态,需要等commit之后,才能执行。
start transaction;
select * from test where id = 1 for update;
update test set score = score + 1 where id = 1;
commit;

适用场景

写多写操作的前提,是保证数据不出错,悲观锁的机制很符合。

优点

强一致性:基于事务又加锁,一致性可以保证。
实现简单:在事务中for update即可,开发者不需要在这上面关注太多。

缺点

死锁风险:悲观锁在使用不当的情况下可能导致死锁。如果多个事务持有锁并相互等待对方释放锁的情况发生,就可能发生死锁。
性能较低:悲观锁通常需要在整个事务过程中锁定资源,这可能导致其他事务阻塞。

模拟实现

前置准备

#创建一个非常简单的表,并插入一条数据
CREATE TABLE `test` (`id` int(11) unsigned NOT NULL AUTO_INCREMENT,`score` int(11) NOT NULL,PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;INSERT INTO `test` (`id`, `score`) VALUES (1, 0);

需求模拟

查询test表id为1的数据,检测到score值为0,则自增,否则终止。

不加锁实现

为了提升性能,使用了原生PDO操作MySQL去实现。

//连接数据库
$pdo = new \PDO("mysql:host=127.0.0.1;port=3306;dbname=temp;", 'root', 'root');
$pdo->setAttribute(\PDO::ATTR_ERRMODE,\PDO::ERRMODE_EXCEPTION);
$pdo->query('set names utf8mb4');//查询
$query = $pdo->query('select score from test');
$query->setFetchMode(\PDO::FETCH_ASSOC);
$res = $query->fetchALL();if($res[0]['score'] == 0) {$res = $pdo->exec('update test set score = score + 1 where id = 1');var_dump($res);
}

并发模拟

用ab压测,发现效果不明显,可能是ab工具不够力或者电脑线程数量太少导致。
这里用的是ApiPost的压测工具。500个并发去多次压测一轮,发现score值是3,证明确实因为并发造成了与预期结果不一致的情况。

乐观锁解决方案(忽略ABA问题)

#将sql改为如下所示,实测多次,score最大值是1
#注意这种行为,只能保证score的值最大是1,无法保证执行这个SQL的时候,受影响行数>0
update test set score = score + 1 where id = 1 and score = 0

悲观锁解决方案

$pdo = new \PDO("mysql:host=127.0.0.1;port=3306;dbname=temp;", 'root', 'root');
$pdo->setAttribute(\PDO::ATTR_ERRMODE,\PDO::ERRMODE_EXCEPTION);
$pdo->query('set names utf8mb4');$redis = new Redis;
$redis->connect('127.0.0.1', 6379);try {$pdo->beginTransaction();$stmt = $pdo->prepare("select * from test where id = 1 for update");$stmt->execute();$res = $stmt->fetch(PDO::FETCH_ASSOC);if($res['score'] == 0) {$stmt = $pdo->prepare("UPDATE test SET score = (score + 1) where id  = 1");$stmt->execute();$pdo->commit();$redis->incr('commit');} else {$redis->incr('rollback');$pdo->rollBack();}
} catch (PDOException $e) {$pdo->rollBack();
}// 关闭数据库连接
$pdo = null;

500个并发压测一轮,查看redis数据,commit数量为1,其余499全部都是rollback,这么多的回滚不代表大错特错(演示效果),而是因为第一个事务执行成功后,再执行其它事务,正因为一个一个排队,就不会出现同时读取多个score值为0的情况了。

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

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

相关文章

Vue+OpenLayers7:html原生网页如何使用OpenLayers7地图

返回《Vue+OpenLayers7》专栏目录:Vue+OpenLayers7 前言 尽管现在大部分网页都是使用Vue或者React开发了,但是还是有不少开发者使用的是网页原生html进行开发,或者是老项目维护的需要,所以为了照顾使用html原生网页的同学们,本章简单讲解一下如何使用原始html网页情况下…

关于网络协议的笔记

简介: 协议, 网络协议的简称,网络协议是通信计算机双方必须共同遵从的一组约定。如怎么样建立连 接、怎么样互相识别等。只有遵守这个约定,计算机之间才能相互通信交流。它的 三要素是:语 法、语义、时序。 为了使数…

外网ssh远程连接服务器

文章目录 外网ssh远程连接服务器一、前言二、配置流程1. 在服务器上安装[cpolar](https://www.cpolar.com/)客户端2. 查看版本号,有正常显示版本号即为安装成功3. token认证4. 简单穿透测试5. 向系统添加服务6. 启动cpolar服务7. 查看服务状态8. 登录后台&#xff0…

常用shell脚本命令总结

可以将shell脚本 当做终端命令的集合,终端可以运行shell就可以 shell 脚本 声明(第一行加) #!/bin/bash 设置变量 FILE_PATH_BASE/home/gitlab-runner/apk_download/ 使用变量 "$FILE_PATH_BASE" 1、mv命令 文件移动 mv ./build/web/ ${FILE_PATH_BASE} …

innodb底层原理和MySQL日志机制

server层 1. 连接器 客户端连接数据库需要输入账号、密码。连接器进行校验账号密码以及权限。 2. 查询缓存 连接器连接以后,比如输入一个select语句,这时候第一步就会先根据sql语句作为key给查询缓存中查看这条sql有没有已经被查询过,如果…

k8s图形化管理工具之rancher

前言 在前面的k8s基础学习中,我们学习了各种资源的搭配运用,以及命令行,声明式文件创建。这些都是为了k8s管理员体会k8s的框架,内容基础。在真正的生产环境中,大部分的公司还是会选用图形化管理工具来管理k8s集群&…

Mybatis 全局配置文件(三)

文章目录 第一章:概述第二章:properties (了解)第三章:settings第四章:typeAliases (别名处理器)第五章:typeHandlers (类型处理器)第六章:plugins(插件)第七章:environments (环境)第八章&…

民安智库-医院职工满意度调查报告如何撰写

撰写医院职工满意度调查报告是整个调查过程的重要步骤,它可以帮助医院管理层理解员工的反馈并制定改进计划。以下是撰写医院职工满意度调查报告的一般步骤和建议: 1.报告概述:开始报告时,提供一个简要的概述,包括报告…

顺序表和链表【数据结构】【基于C语言实现】【一站式速通】

目录 顺序表 顺序表的优点 顺序表的实现 1.结构体的定义 2.初始化数组 3.插入数据 4.其余接口函数的实现 5.释放内存 顺序表的缺陷 单向链表 单向链表的优点 单向链表的实现 1.链表的定义 2.链表的初始化 3.其余接口函数的实现 5.释放内存 单向链表的缺陷 双…

Docker(九)Docker Buildx

作者主页: 正函数的个人主页 文章收录专栏: Docker 欢迎大家点赞 👍 收藏 ⭐ 加关注哦! Docker Buildx Docker Buildx 是一个 docker CLI 插件,其扩展了 docker 命令,支持 [Moby BuildKit] 提供的功能。提…

day04-CSS进阶

01-复合选择器 定义:由两个或多个基础选择器,通过不同的方式组合而成。 作用:更准确、更高效的选择目标元素(标签)。 后代选择器 后代选择器:选中某元素的后代元素。 选择器写法:父选择器 …

Java设计模式-桥接模式(9)

馆长准备了很多学习资料,其中包含java方面,jvm调优,spring / spring boot /spring cloud ,微服务,分布式,前端,js书籍资料,视频资料,以及各类常用软件工具,破解工具 等资源。请关注“IT技术馆”公众号,进行关注,馆长会每天更新资源和更新技术文章等。请大家多多关注…

Java线程池七大参数详解和配置(面试重点!!!)

一、corePoolSize核心线程数 二、maximunPoolSize最大线程数 三、keepAliveTime空闲线程存活时间 四、unit空闲线程存活时间的单位 五、workQueue线程工作队列 1、ArrayBlockingQueue FIFO有界阻塞队列 2、LinkedBlockingQueue FIFO无限队列 3、PriorityBlockingQueue V…

竞赛保研 车道线检测(自动驾驶 机器视觉)

0 前言 无人驾驶技术是机器学习为主的一门前沿领域,在无人驾驶领域中机器学习的各种算法随处可见,今天学长给大家介绍无人驾驶技术中的车道线检测。 1 车道线检测 在无人驾驶领域每一个任务都是相当复杂,看上去无从下手。那么面对这样极其…

教学改进措施及方法

在教育的世界里,每一位教师都是一位探险家,探索着如何更好地点燃学生的求知欲望,帮助他们展翅飞翔。我,作为一位拥有多年教学经验的教师,也在这条路上不断摸索。今天,我想分享一些我在教学实践中的改进措施…

ai伪原创生成器app,一键生成原创文章

近年来,随着人工智能技术的飞速发展,AI伪原创生成器App已经成为了许多写手和创作者们的新宠。这款AI伪原创生成器App以其一键生成原创文章的快速便捷性,正在引起广泛的关注和使用。下面跟随小编一起来了解下吧! 随着互联网的普及&…

洛谷------------------B2112石头剪子布

题目描述 : 石头剪子布,是一种猜拳游戏。起源于中国,然后传到日本、朝鲜等地,随着亚欧贸易的不断发展它传到了欧洲,到了近现代逐渐风靡世界。简单明了的规则,使得石头剪子布没有任何规则漏洞可钻&#xff0…

Transformer and Pretrain Language Models3-5

Transformer结构(优化Tricks) Transformer在训练和生成过程中,采用了很多小技巧: 首先是训练过程,训练过程中采用了一种叫checkpoint average技术,以及ADAM的一个优化器来进行参数更新,另外的…

【QML-Qt Design Studio】

QML编程指南 ■ Qt Design Studio (Qt Quick UI设计工具)■ 安装Qt Design Studio■ ■ Qt Design Studio (Qt Quick UI设计工具) Qt Design Studio是一个用于创建酷炫、优美UI的工具。 简单概括其功能就是让UI设计转换为qml&…

《WebKit 技术内幕》学习之十二(2):安全机制

2 沙箱模型 2.1 原理 一般而言,对于网络上的网页中的JavaScript代码和插件是不受信的(除非是经过认证的网站),特别是一些故意设计侵入浏览器运行的主机代码更是非常危险,通过一些手段或者浏览器中的漏洞&#xff0c…