MySQL事务隔离级别+共享锁,排他锁,乐观锁,悲观锁

news2024/12/23 17:25:12

在操作数据库的时候,可能会由于并发问题而引起的数据的不一致性(数据冲突)。

MySQL事务隔离级别

一个事务的执行,本质上就是一条工作线程在执行,当出现多个事务同时执行时,这种情况则被称之为并发事务,所谓的并发事务也就是指多条线程并发执行。
多线程并发执行自然就会出问题,也就是脏写、脏读、不可重复读及幻读问题。而对于这些问题又可以通过调整事务的隔离级别来避免,那为什么调整事务的隔离级别后能避免这些问题产生呢?这是因为不同的隔离级别中,工作线程执行SQL语句时,用的锁粒度、类型不同。
在这里插入图片描述
并发事务会带来哪些问题?

  • 1、脏读:事务A读取了事务B更新的数据,然后B回滚操作,那么A读取到的数据是脏数据

  • 2、不可重复读:事务 A 多次读取同一数据,事务 B 在事务A多次读取的过程中,对数据作了更新并提交,导致事务A多次读取同一数据时,结果 不一致。

  • 3、幻读:系统管理员A将数据库中所有学生的成绩从具体分数改为ABCDE等级,但是系统管理员B就在这个时候插入了一条具体分数的记录,当系统管理员A改结束后发现还有一条记录没有改过来,就好像发生了幻觉一样,这就叫幻读。

总结:不可重复读的和幻读很容易混淆,不可重复读侧重于修改,幻读侧重于新增或删除。解决不可重复读的问题只需锁住满足条件的行,解决幻读需要锁表
在博客中间部分mysql事务隔离级别例子讲得很详细

用例子说明各个隔离级别的情况

1、读未提交:

  • (1)打开一个客户端A,并设置当前事务模式为read uncommitted(未提交读),查询表account的初始值
  • (2)在客户端A的事务提交之前,打开另一个客户端B,更新表account400-50=350
  • (3)这时,虽然客户端B的事务还没提交,但是客户端A就可以查询到B已经更新的数据
  • (4)一旦客户端B的事务因为某种原因回滚,所有的操作都将会被撤销,那客户端A查询到的数据其实就是脏数据

2、读已提交

  • (1)打开一个客户端A,并设置当前事务模式为read committed(不可重复读),查询表account的所有记录
  • (2)在客户端A的事务提交之前,打开另一个客户端B,更新表account
  • (3)这时,客户端B的事务还没提交,客户端A不能查询到B已经更新的数据,解决了脏读问题
  • (4)客户端B的事务提交commit
  • (5)客户端A执行与上一步相同的查询,结果 与上一步不一致,即产生了不可重复读的问题

3、可重复读

  • (1)打开一个客户端A,并设置当前事务模式为repeatable read,查询表account的所有记录400
  • (2)在客户端A的事务提交之前,打开另一个客户端B,更新表account400-50=350并提交
  • (3)在客户端A查询表account的所有记录,与步骤(1)查询结果400一致,没有出现不可重复读的问题
  • (4)在客户端A,接着执行update balance = balance - 50 where id = 1,balance没有变成400-50=350,这里的balance值用的是步骤2中的350来算的,所以是300,数据的一致性倒是没有被破坏。可重复读的隔离级别下使用了MVCC机制,select操作不会更新版本号,是快照读(历史版本);insert、update和delete会更新版本号,是当前读(当前版本)
  • (5)重新打开客户端B,插入一条新数据后提交
  • (6)在客户端A查询表account的所有记录,没有 查出 新增数据,所以没有出现幻读(??不是会幻读吗??)

4.串行化(serializable)

  • (1)打开一个客户端A,并设置当前事务模式为serializable,查询表account的初始值:
  • (2)打开一个客户端B,并设置当前事务模式为serializable,插入一条记录报错,表被锁了插入失败,mysql中事务隔离级别为serializable时会锁表,因此不会出现幻读的情况,这种隔离级别并发性极低,开发中很少会用到

补充:

  • 1、事务隔离级别为读提交时,写数据只会锁住相应的行
  • 2、事务隔离级别为可重复读时,如果检索条件有索引(包括主键索引)的时候,默认加锁方式是next-key 锁;如果检索条件没有索引,更新数据时会锁住整张表。一个间隙被事务加了锁,其他事务是不能在这个间隙插入记录的,这样可以防止幻读。
  • 3、事务隔离级别为串行化时,读写数据都会锁住整张表
  • 4、隔离级别越高,越能保证数据的完整性和一致性,但是对并发性能的影响也越大。

MySQL的锁体系:

这篇讲得超详细——MySQL锁、加锁机制(超详细)
以锁粒度的维度划分
全局锁:锁定数据库中的所有表。加上全局锁之后,整个数据库只能允许读,不允许做任何写操作
表级锁:每次操作锁住整张表。主要分为三类
表锁(分为表共享读锁 read lock、表独占写锁 write lock)
元数据锁(meta data lock,MDL):基于表的元数据加锁,加锁后整张表不允许其他事务操作。这里的元数据可以简单理解为一张表的表结构
意向锁(分为意向共享锁、意向排他锁):这个是InnoDB中为了支持多粒度的锁,为了兼容行锁、表锁而设计的,使得表锁不用检查每行数据是否加锁,使用意向锁来减少表锁的检查
行级锁:每次操作锁住对应的行数据。主要分为三类
记录锁 / Record 锁:也就是行锁,一条记录和一行数据是同一个意思。防止其他事务对此行进行update和delete,在 RC、RR隔离级别下都支持
间隙锁 / Gap 锁:锁定索引记录间隙(不含该记录),确保索引记录间隙不变,防止其他事务在这个间隙进行insert,产生幻读。在RR隔离级别下都支持
临键锁 / Next-Key 锁:间隙锁的升级版,同时具备记录锁+间隙锁的功能,在RR隔离级别下支持
以互斥性的角度划分
共享锁 / S锁:不同事务之间不会相互排斥、可以同时获取的锁
排他锁 / X锁:不同事务之间会相互排斥、同时只能允许一个事务获取的锁
共享排他锁 / SX锁:MySQL5.7版本中新引入的锁,主要是解决SMO带来的问题
以操作类型的维度划分
读锁:查询数据时使用的锁
写锁:执行插入、删除、修改、DDL语句时使用的锁
以加锁方式的维度划分
显示锁:编写SQL语句时,手动指定加锁的粒度
隐式锁:执行SQL语句时,根据隔离级别自动为SQL操作加锁
以思想的维度划分
乐观锁:每次执行前认为自己会成功,因此先尝试执行,失败时再获取锁
悲观锁:每次执行前都认为自己无法成功,因此会先获取锁,然后再执行

放眼望下来,是不是看着还蛮多的,但总归说来说去其实就共享锁、排他锁两种,只是加的方式不同、加的地方不同,因此就演化出了这么多锁的称呼。

1.共享锁【S锁】

数据库自带的锁。
又称读锁,若事务T对数据对象A加上S锁,则事务T可以读A但不能修改A,其他事务只能再对A加S锁,而不能加X锁,直到T释放A上的S锁。这保证了其他事务可以读A,但在T释放A上的S锁之前不能对A做任何修改。

上共享锁的写法:lock in share mode

例如: select math from zje where math>60 lock in share mode;

2.排他锁【X锁】

数据库自带的锁。
又称写锁。若事务T对数据对象A加上X锁,事务T可以读A也可以修改A,其他事务不能再对A加任何锁,直到T释放A上的锁。
确保不会同时同一资源进行多重更新。
如果事务T对数据A加上排他锁后,则其他事务不能再对A加任任何类型的封锁。获准排他锁的事务既能读数据,又能修改数据。

上排它锁的写法:for update

例如:select math from zje where math >60 for update;

对比

  • 共享锁就是允许多个线程同时获取一个锁,一个锁可以同时被多个线程拥有。
  • 排它锁,也称作独占锁,一个锁在某一时刻只能被一个线程占有,其它线程必须等待锁被释放之后才可能获取到锁。

排它锁和共享锁实例

ReentrantLock就是一种排它锁。CountDownLatch是一种共享锁。这两类都是单纯的一类,即,要么是排它锁,要么是共享锁。 ReentrantReadWriteLock是同时包含排它锁和共享锁特性的一种锁,这里主要以ReentrantReadWriteLock为例来进行分析学习。我们使用ReentrantReadWriteLock的写锁时,使用的便是排它锁的特性;使用ReentrantReadWriteLock的读锁时,使用的便是共享锁的特性。

3.乐观锁

乐观锁不是数据库自带的,需要我们自己去实现。乐观锁是指操作数据库时(更新操作),想法很乐观,认为这次的操作不会导致冲突,在操作数据时,并不进行任何其他的特殊处理(也就是不加锁),而在进行更新后,再去判断是否有冲突。

使用乐观锁需修改数据库的事务隔离级别:
使用乐观锁的时候,如果一个事务修改了库存并提交了事务,那其他的事务应该可以读取到修改后的数据值,所以不能使用可重复读的隔离级别,应该修改为读取已提交(Read committed)。

(1)版本号法实现乐观锁

在商品表中增加一个版本号字段,每次更新库存时,都会携带这个版本号。如果版本号没有发生变化,说明在此期间没有其他线程修改过数据,更新操作可以进行;如果版本号发生了变化,则说明有其他线程已经修改过数据,此时需要重新获取最新的数据并尝试再次更新。

"UPDATE goods SET stock = stock - 1, version = version + 1 WHERE id = #{id} AND stock > 0 AND version = #{version}
//当商品的库存大于0且版本号与传入的版本号相同时,将库存减1,并将版本号加1。

(2)CAS法实现乐观锁

CAS用库存量代替版本号,在执行操作时判断 where 用的库存量和在最初查询的时候,是否相等。
加乐观锁:先比较再更新,更新的时候判断**此时的库存是否是之前查询出的库存,**如果相同,表示没人修改,可以更新库存,否则表示别人抢过资源,不再执行库存更新。

CAS

CAS 的全称是 Compare And Swap(比较与交换) ,用于实现乐观锁,被广泛应用于各大框架中。CAS 的思想很简单,就是用一个预期值和要更新的变量值进行比较,两值相等才会进行更新。

CAS 是一个原子操作,底层依赖于一条 CPU 的原子指令。
原子操作 即最小不可拆分的操作,也就是说操作一旦开始,就不能被打断,直到操作完成。

  • CAS的自旋:
    自旋: 就是不停的判断比较,看能否将值交换
    在CAS操作中,自旋的概念指的是当线程发现无法立即完成操作时,不会让出CPU时间片,而是继续循环尝试,直到成功为止。这种机制可以避免线程频繁地挂起和恢复,减少了线程切换的开销,提高了效率。自旋锁通常适用于锁被占用的时间较短的场景,因为长时间的自旋会导致CPU资源的浪费。

总的来说,CAS的自旋是通过不断循环尝试来实现的一种锁优化机制,它在多线程编程中用于保证操作的原子性和提高性能。

  • CAS/乐观锁存在的问题(ABA):
    CAS 三大问题:
  • (1)ABA问题。CAS需要在操作值的时候检查内存值是否发生变化,没有发生变化才会更新内存值。但是如果内存值原来是A,后来变成了B,然后又变成了A,那么CAS进行检查时会发现值没有发生变化,但是实际上是有变化的。ABA问题的解决思路就是在变量前面添加版本号,每次变量更新的时候都把版本号加一,这样变化过程就从A-B-A变成了1A-2B-3A。JDK从1.5开始提供了AtomicStampedReference类来解决ABA问题,原子更新带有版本号的引用类型。
  • (2)循环时间长开销大。CAS操作如果长时间不成功,会导致其一直自旋,给CPU带来非常大的开销。
    只能保证一个共享变量的原子操作。对一个共享变量执行操作时,CAS能够保证原子操作,但是对多个共享变量操作时,CAS是无法保证操作的原子性的。

乐观锁在高并发场景下的问题:
乐观锁在高并发场景下的问题,是严重的空自旋
自旋: 就是不停的判断比较,看能否将值交换

4.悲观锁

悲观锁就是在操作数据时,认为此操作会出现数据冲突,所以在进行每次操作时都要通过获取锁才能进行对相同数据的操作,这点跟java中的synchronized很相似,所以悲观锁需要耗费较多的时间。另外与乐观锁相对应的,悲观锁是由数据库自己实现了的,要用的时候,我们直接调用数据库的相关语句就可以了。

适用场景:

  • 悲观锁适合写操作多的场景。
  • 乐观锁适合读操作多的场景,不加锁可以提升读操作的性能。

当查询某条记录时,即让数据库为该记录加锁,锁住记录后别人无法操作,使用类似如下语法:
select for update,提前加上写锁

beginTranse(开启事务)
try{
    query('select amount from s_store where goodID = 12345   for update');//加上写锁
    if(库存 > 0){
        //quantity为请求减掉的库存数量
        query('update s_store set amount = amount - quantity where goodID = 12345');
    }
}catch( Exception e ){
    rollBack(回滚)
}
commit(提交事务)

5.行锁

行锁,由字面意思理解,就是给某一行加上锁,也就是一条记录加上锁。

6.表锁

对表操作的,所以自然锁住全表的表锁就不会出现死锁。但是表锁效率低。

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

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

相关文章

node 如何运行typescript

文章目录 node 如何运行typescript手工编译 TypeScript 然后运行 JavaScript使用 ts-node创建一个typescript node项目tsconfig.json node 如何运行typescript 在 Node.js 中运行 TypeScript 代码有几种常见的方法,以下是其中几种。 手工编译 TypeScript 然后运行 JavaScript…

Docker---最详细的服务部署案例

提供python服务的docker一键部署,示例已配置负载均衡,不需要的在nginx.conf和docker-compose注释相关代码即可 文件结构 1、dockerfile # 服务的dockerfile# 服务依赖的镜像 FROM python:3.7# 设置容器内服务的工作目录 WORKDIR /app# 复制当前文件夹所…

数据库-三范式

第一范式 1 数据库所有字段都只有单一属性。 2 单一属性由基本数据类型构成。 3 数据库的表都是二维的行与列。 例如上面的例子就不满足第一范式,因为是可以继续拆分的,拆分为更多的属性。 第二范式 1 符合第一范式 2 表必须有个主建 3 其它字段可以…

企业网络运维-给华为交换机配置sftp,浏览交换机文件并下载上传

文章目录 需求实验开户stelnet权限已完成stelnet账号下的sftp配置使用xshell-sftp访问 需求 浏览交换机文件并下载上传 实验 开户stelnet权限 参考https://blog.csdn.net/xzzteach/article/details/140419150 已完成stelnet账号下的sftp配置 服务类型all包括stelnet和sf…

SadTalker数字人服务器部署

一、单独SadTalker部署 git clone https://github.com/OpenTalker/SadTalker.gitcd SadTalker conda create -n sadtalker python3.8conda activate sadtalkerpip install torch1.12.1cu113 torchvision0.13.1cu113 torchaudio0.12.1 --extra-index-url https://download.pyto…

NSSCTF中24网安培训day2中web题目

[SWPUCTF 2021 新生赛]ez_unserialize 这道题目考察php反序列化的知识点 打开题目,发现没有提示,我们试着用御剑扫描目录文件,发现存在robots.txt的文件 接着访问这个文件,发现是一段php反序列化代码,我们需要进行序…

[Windows] 油.管视频下载神器 Gihosoft TubeGet Pro v9.3.88

描述 对于经常在互联网上进行操作的学生,白领等! 一款好用的软件总是能得心应手,事半功倍。 今天给大家带了一款高科技软件 管视频下载神器 无需额外付费,永久免费! 亲测可运行!! 内容 目前主…

【B树、B-树、B+树】

目录 一、B-树(即B树)的定义及操作1.1、定义1.2、操作1.2.1、查找1.2.2、插入1.2.3、删除 二、B树的定义及操作2.1、定义2.2、操作2.2.1、查找2.2.2、插入2.2.3、删除 一、B-树(即B树)的定义及操作 1.1、定义 B-tree即B树&#…

jmeter-beanshell学习10-字符串补齐位数

每天都遇到新问题,今天又一个场景,一个字符串,如果不足11位,则左边补0,补够11位。 先要获取字符串长度,然后计算差多少位,补齐。今天又发现一个Object类型,这个类型有点厉害&#x…

uniapp启动图延时效果,启动图的配置

今天阐述uniapp开发中给启动图做延迟效果,不然启动图太快了,一闪就过去了; 一:修改配置文件:manifest.json "app-plus" : {"splashscreen" : {"alwaysShowBeforeRender" : false,"…

dhtmlx-gantt甘特图数据展示

官网文档&#xff1a;甘特图文档 实现效果&#xff1a; 首先需要下载 dhtmlx-gantt组件 npm i dhtmlx-gantt //我项目中使用的是"dhtmlx-gantt": "^8.0.6" 这个版本&#xff0c;不同的版本api或是文档中存在的方法稍有差异 界面引用 <template>&l…

二叉搜索树的实现[C++]

文章目录 搜索二叉树概念二叉搜索树的功能查找 实现搜索二叉树节点的定义建立搜索二叉树接口插入搜索打印删除 总结 今天本堂主来一起讨论下什么是搜索二叉树&#xff0c;和如何实现二叉搜索树 搜索二叉树 那么二叉搜索树似乎如何实现搜索呢&#xff1f;二叉搜索树和普通二叉…

PNPM 高效入门:安装配置一本通

PNPM高效入门&#xff1a;安装配置一本通 引言Pnpm 简介安装 PNPM全局安装&#xff08;推荐&#xff09;使用 nvm&#xff08;Node Version Manager&#xff09; 配置PNPM使用PNPM管理项目初始化项目 添加依赖快速安装所有依赖查看安装的包 优化与故障排除PNPM与持续集成/持续部…

nacos 适配瀚高数据库、ARM 架构

下载nacos源码: https://github.com/alibaba/nacos/tree/2.3.1 瀚高技术文档 1、修改pom.xml 根目录nacos-all => pom.xml<dependencyManagement><dependency><groupId>com.highgo</groupId><artifactId>HgdbJdbc</artifactId><…

安全防御:防火墙基本模块

目录 一、接口 1.1 物理接口 1.2 虚拟接口 二、区域 三、模式 3.1 路由模式 3.2 透明模式 3.3 旁路检测模式 3.4 混合模式 四、安全策略 五、防火墙的状态检测和会话表技术 一、接口 1.1 物理接口 三层口 --- 可以配置IP地址的接口 二层口&#xff1a; 普通二层…

linux的学习(四):磁盘,进程,定时,软件包的相关命令

简介 关于磁盘管理&#xff0c;进程管理&#xff0c;定时任务&#xff0c;软件包管理的命令的使用 磁盘管理类命令 du du 目录名&#xff1a; 查看文件和目录占用的磁盘空间 参数&#xff1a; -h&#xff1a;可以看到大小的单位&#xff0c;g,mb-a&#xff1a;还可以看到文…

Mapboxgl 生成飞行动画GIF

更多精彩内容尽在数字孪生平台&#xff0c;关注公众号【sky的数孪技术】&#xff0c;技术交流、源码下载请添加VX&#xff1a;digital_twin123 根据两点生成动画的工具。 首先&#xff0c;找到你想要开始的视图&#xff0c;点击“设置起点视图”&#xff0c;然后调整到目的视图…

Android中RecyclerView使用详解(一)

目录 概述优点列表布局RecyclerView一、创建RecyclerView并且在布局中绑定二、实现RecyclerView单个item的布局三、给RecyclerView写一个对应的适配器Adapter1.创建自定义的ViewHolder2.继承Adapter&#xff0c;泛型使用我们自定义的ViewHolder3.重写Adapter的三个方法onCreate…

如何提取视频中的音频?提取音频的几种方法

如何提取视频中的音频&#xff1f;提取视频中的音频&#xff0c;是许多人在处理多媒体内容时常遇到的需求。这一过程不仅仅是简单地从视听媒体中抽离音频部分&#xff0c;它背后蕴含着许多技术上的挑战和创意上的可能性。通过提取音频&#xff0c;你可以更方便地利用视频中的声…

网络安全——防御(防火墙)带宽以及双机热备实验

12&#xff0c;对现有网络进行改造升级&#xff0c;将当个防火墙组网改成双机热备的组网形式&#xff0c;做负载分担模式&#xff0c;游客区和DMZ区走FW3&#xff0c;生产区和办公区的流量走FW1 13&#xff0c;办公区上网用户限制流量不超过100M&#xff0c;其中销售部人员在其…