MySQL事务和锁

news2025/1/13 10:21:18

目录

1、四大特性

2、事务引发的问题

3、事务控制演进

3.1、排队

3.2、排它锁

3.3、读写锁

3.4、MVCC

4、事务的隔离级别

4.1、四种隔离级别

4.2、事务隔离级别和锁的关系

4.3、MySQL隔离级别控制

5、锁机制和实战

5.1、锁分类

5.1.1、按操作粒度分类

5.1.2、按操作类型分类

5.1.3、按操作性能分类

5.2、行锁原理(InnoDb)

5.2.1、常见SQL加锁

5.2.2、举例

5.3、悲观锁

5.3.1、表级锁

5.3.2、共享锁(行级锁,读锁)

5.3.3、排它锁(行级锁,写锁)

5.4、乐观锁

5.4.1、实现原理

5.5、死锁与解决方案

5.5.1、表锁死锁

5.5.2、行锁死锁

5.5.3、共享锁转换为排它锁

5.5.4、锁排查


        逻辑上的一组操作,要么都执行,要么都不执行。原子性、一致性、隔离性、持久性。

1、四大特性

  1. 原子性: 事务是小的执行单位,不允许分割。事务的原子性确保动作要么全部完成,要么完全不起作用;
  2. 一致性: 执行事务前后,数据保持一致,多个事务对同一个数据读取的结果是相同的;如果没有原子性的保证,在发生数据库系统故障的情况下,数据库就有可能处于不一致状态(数据提交一半,系统故障,另一半未提交);
  3. 隔离性: 并发访问数据库时,一个用户的事务不被其他事务所干扰,各并发事务之间数据库是独立的;
  4. 持久性: 一个事务被提交之后。它对数据库中数据的改变是持久的,不能回滚。即使数据库发生故障也不应该对其有任何影响。

2、事务引发的问题

  • 脏读

        一个事务读取了另一个事务尚未提交的数据。

        初值age=10,事务A修改age=20,事务B读取age=20,事务A回滚age=10。

  • 丢失更新

        两个事务同时更新一行数据,最后一个事务的更新会覆盖前一个事务的更新。

        事务A更新age=50 ->> 事务B更新age=30。事务A的更新被覆盖。

  • 幻读

        事务多次读取同一个数据,数据总量不一致。(前后多次读取,数据总量不一致)

        事务A查询数据总量100条,事务B新增数据100条,数据A查询总量为200条。

  • 不可重复读

        事务多次读取同一条数据,数据内容不一样。(前后多次读取,数据内容不一样)

        事务A读age=10,事务B修改age=20,事务A读age=20。

        不可重复读的重点是修改,幻读的重点是新增或删除。

3、事务控制演进

排队->排它锁->读写锁->MVCC

3.1、排队

        所有事务操作依次排队处理。串行化,效率低。

3.2、排它锁

        互斥锁,如果事务涉及到相同数据,先进入的事务给数据加锁,其他事务被阻塞。

3.3、读写锁

        读读之间不加锁,读写、写读、写写之间加排它锁。

3.4、MVCC

MVCC多版本并发控制_零点冰.的博客-CSDN博客

4、事务的隔离级别

4.1、四种隔离级别

Read uncommitted(读未提交)

        一个事务可以读取另一个未提交事务的数据。

        脏读、不可重复读、幻读。

Read committed(读已提交)

        若存在事务A进行更新操作时,读事务B会等到事务A提交后才能读取数据。

        不可重复读、幻读。

Repeatable read(重复读)

        在开始读取数据(开启事务)后,不再允许修改(update)操作,但不能阻止insert操作。

幻读。

Serializable(串行化)

        事务串行化执行。

Mysql的InnoDB引擎使用的是Repeatable read(重复读)

SQL Server和Oracle默认隔离级别为Read Committed(读已提交)

4.2、事务隔离级别和锁的关系

  • 事务隔离级别本质上是对锁和MVCC使用的封装,隐藏了底层细节。
  • 锁是数据库实现并发控制的基础,事务隔离性是采用锁来实现,对相应操作加不同的锁,就可以防止其他事务同时对数据进行读写操作。
  • 对用户来讲,首先选择使用隔离级别,当选用的隔离级别不能解决并发问题或需求时,才有必要在开发中手动的设置锁

4.3、MySQL隔离级别控制

  • 查看当前数据库隔离级别
show variables like 'tx_isolation';

或

select @@tx_isolation;
  • 设置数据库隔离级别

5、锁机制和实战

5.1、锁分类

5.1.1、按操作粒度分类

  • 表级锁:每次操作锁住整张表。锁定粒度大,发生锁冲突的概率最高,并发度最低。应用在MyISAMInnoDBBDB 等存储引擎中。
  • 行级锁:每次操作锁住一行数据。锁定粒度最小,发生锁冲突的概率最低,并发度最高。应用在InnoDB 存储引擎中。
  • 页级锁:每次锁定相邻的一组记录,锁定粒度界于表锁和行锁之间,开销和加锁时间界于表锁和行锁之间,并发度一般。应用在BDB 存储引擎中。

5.1.2、按操作类型分类

  • 读锁(S锁):共享锁,针对同一份数据,多个读操作可以同时进行而不会互相影响。
  • 写锁(X锁):排他锁,当前写操作没有完成前,它会阻断其他写锁和读锁。

5.1.3、按操作性能分类

  • 乐观锁:一般的实现方式是对记录数据版本进行比对,在数据更新提交的时候才会进行冲突检测,如果发现冲突了,则提示错误信息。
  • 悲观锁:在对一条数据修改的时候,为了避免同时被其他人修改,在修改数据之前先锁定,再修改的控制方式。共享锁和排他锁是悲观锁的不同实现,但都属于悲观锁范畴

5.2、行锁原理(InnoDb)

行锁又分为共享锁和排他锁。

行锁->记录锁 + 间隙锁 + (记录锁+范围锁)

InnoDB行锁是通过对索引数据页上的记录加锁实现的,主要实现算法有 3 种:Record LockGap Lock Next-key Lock

  • RecordLock锁:锁定单个行记录的锁。(记录锁,RCRR隔离级别都支持)
  • GapLock锁:间隙锁,锁定索引记录间隙,确保索引记录的间隙不变。(范围锁,RR隔离级别支持);

        锁的是索引叶子节点的next指针,或者说间隙锁是一个在索引记录之间的间隙上的锁;

        解决了mysql重复读级别下的幻读问题;

  • Next-key Lock 锁:记录锁和间隙锁组合,同时锁住数据,并且锁住数据前后范围。(记录锁+范围锁,RR隔离级别支持)

RR隔离级别InnoDB对于记录加锁行为都是先采用Next-Key Lock,但是当SQL操作含有唯一索引时Innodb会对Next-Key Lock进行优化,降级为RecordLock,仅锁住索引本身而非范围。

(RR隔离级别,优先加记录锁和间隙锁,当SQL有唯一索引时,才降级为记录锁)。

5.2.1、常见SQL加锁

        普通select查询不加锁,insert语句加记录锁,其余SQL优先使用Next-key Lock锁,有唯一索引时降级为记录锁。

  1. select ... from 语句:InnoDB引擎采用MVCC机制实现非阻塞读,所以对于普通的select语句,InnoDB不加锁
  2. select ... from lock in share mode语句:追加了共享锁,InnoDB会使用Next-Key Lock锁进行处理,如果扫描发现唯一索引,可以降级为RecordLock锁。
  3. select ... from for update语句:追加了排他锁,InnoDB会使用Next-Key Lock锁进行处理,如果扫描发现唯一索引,可以降级为RecordLock锁。
  4. update ... where 语句:InnoDB会使用Next-Key Lock锁进行处理,如果扫描发现唯一索引,可以降级为RecordLock锁。
  5. delete ... where 语句:InnoDB会使用Next-Key Lock锁进行处理,如果扫描发现唯一索引,可以降级为RecordLock锁。
  6. insert语句:InnoDB会在将要插入的那一行设置一个排他的RecordLock锁。

5.2.2、举例

        以“update t1 set name=‘XX’ where id=10”操作为例,举例子分析下 InnoDB 对不同索引的加锁行为,以RR隔离级别为例。

  • 主键id加锁

        加锁行为:仅在id=10的主键索引记录上加

        

  •  唯一键id加锁

        加锁行为:先在唯一索引id上加X锁,然后在id=10的主键索引记录上加锁。

        

  •  非唯一键加锁

        加锁行为:对满足id=10条件的记录和主键分别加X锁,然后在(6,c)-(10,b)(10,b)-(10,d)(10,d)-(11,f)范围分别加Gap Lock

        

  •  无索引加锁

        加锁行为:表里所有行和间隙都会加X锁。(当没有索引时,会导致全表锁定,因为InnoDB引擎锁机制是基于索引实现的记录锁定)。

        

5.3、悲观锁

数据处理时,每次都锁定当前数据。

悲观锁:行锁、表锁、读锁、写锁、共享锁、排它锁。

5.3.1、表级锁

        锁整张表,并发度低。

  • 增加表锁:lock table 表名称 read|write,表名称2 read|write;
  • 查看表锁:show open tables;
  • 删除表锁:unlock tables;

        表级读锁:当前表追加read锁,当前连接和其他的连接都可以读操作;但是当前连接增删改操作会报错,其他连接增删改会被阻塞。

        表级写锁:当前表追加write锁,当前连接可以对表做增删改查操作,其他连接对该表所有操作都被阻塞(包括查询)。

5.3.2、共享锁(行级锁,读锁)

        行锁-读锁,多个事务对于同一数据可以共享一把锁,都能访问到数据,但是只能读不能修改。使用共享锁的方法是在select ... lock in share mode,只适用查询语句

事务使用了共享锁(读锁),只能读取,不能修改,修改操作被阻塞。

5.3.3、排它锁(行级锁,写锁)

        行锁-写锁,互斥,针对同一行数据,不同事务不能同时进行读/写操作。

        使用排他锁的方法是在SQL末尾加上for updateinnodb引擎默认会在updatedelete语句加上for update。行级锁的实现其实是依靠其对应的索引,所以如果操作没用到索引的查询,那么会锁住全表记录

        事务使用了排他锁(写锁),当前事务可以读取和修改,其他事务不能修改,也不能获取记录锁(select... for update)。如果查询没有使用到索引,将会锁住整个表记录。

5.4、乐观锁

不加锁,而是在事务提交时,再去判断数据是否有冲突。实现关键点在于冲突的检测

5.4.1、实现原理

  • 使用版本字段

        先给数据表增加一个版本(version) 字段,每操作一次,将那条记录的版本号加 1。version是用来查看被读的记录有无变化,作用是防止记录在业务处理期间被其他事务修改。

  • 使用时间戳

        与使用version版本字段相似,同样需要给在数据表增加一个字段,字段类型使用timestamp时间戳。也是在更新提交的时候检查当前数据库中数据的时间戳和自己更新前取到的时间戳进行对比,如果一致则提交更新,否则就是版本冲突,取消操作。

5.5、死锁与解决方案

5.5.1、表锁死锁

  • 产生原因

        用户A访问表A(锁住了表A),然后又访问表B;另一个用户B访问表B(锁住了表B),然后企图访问表A;这时用户A由于用户B已经锁住表B,它必须等待用户B释放表B才能继续,同样用户B要等用户A释放表A才能继续,这就死锁就产生了。

        用户A--》A表(表锁)--》B表(表锁)

        用户B--》B表(表锁)--》A表(表锁)

  • 解决方案

        程序bug,无法解决,只能调整程序逻辑。对于数据库的多表操作时,尽量按照相同的顺序进行处理,尽量避免同时锁定两个资源。

5.5.2、行锁死锁

  • 产生原因1

        在事务中执行了一条没有索引条件的查询,引发全表扫描,把行级锁上升为全表记录锁定(等价于表级锁),多个这样的事务执行后,就很容易产生死锁和阻塞,最终应用系统会越来越慢,发生阻塞或死锁。

  • 解决方案1

        SQL语句中不要使用太复杂的关联多表的查询;使用explain“执行计划"对SQL语句进行分析,对于有全表扫描和全表锁定的SQL语句,建立相应的索引进行优化。

  • 产生原因2

        两个事务分别想拿到对方持有的锁,互相等待,于是产生死锁。

        

  • 解决方案2

在同一个事务中,尽可能做到一次锁定所需要的所有资源

按照id对资源排序,然后按顺序进行处理

5.5.3、共享锁转换为排它锁

        事务A 查询一条纪录,然后更新该条纪录;此时事务B 也更新该条纪录,这时事务B 的排他锁由于事务A 有共享锁,必须等A 释放共享锁后才可以获取,只能排队等待。事务A 再执行更新操作时,此处发生死锁,因为事务A 需要排他锁来做更新操作。但是,无法授予该锁请求,因为事务B 已经有一个排他锁请求,并且正在等待事务A 释放其共享锁。

事务A: select * from dept where deptno=1 lock in share mode; //共享锁,1

update dept set dname='java' where deptno=1;//需将共享锁升级为排他锁,但步骤二等待共享锁中,无法升级,造成死锁3

事务B: update dept set dname='Java' where deptno=1;//由于1有共享锁,没法获取排他锁,需等待,2

解决方案:

        对于按钮等控件,点击立刻失效,不让用户重复点击,避免引发同时对同一条记录多次操作;

        使用乐观锁进行控制。乐观锁机制避免了长事务中的数据库加锁开销,大大提升了大并发量下的系统性能。需要注意的是,由于乐观锁机制是在我们的系统中实现,来自外部系统的用户更新操作不受我们系统的控制,因此可能会造成脏数据被更新到数据库中。

5.5.4、锁排查

  • 查看死锁日志
show engine innodb status\G
  • 查看锁状态变量
show status like 'innodb_row_lock%'

Innodb_row_lock_current_waits:当前正在等待锁的数量

Innodb_row_lock_time:从系统启动到现在锁定总时间长度

Innodb_row_lock_time_avg: 每次等待锁的平均时间

Innodb_row_lock_time_max:从系统启动到现在等待最长的一次锁的时间

Innodb_row_lock_waits:系统启动后到现在总共等待的次数

以上内容为个人学习理解,如有问题,欢迎在评论区指出。

部分内容截取自网络,如有侵权,联系作者删除。

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

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

相关文章

CDMP选修课都有什么?

大家都知道CDMP认证考试有四个级别。分别是A级(基础级)P级(实践级)M级(专业级)F级(大师级)。级别越高,考试难度就越大,分数比例要求也更高,相对应…

在Linux命令行中查找空目录

在 Linux 系统中,出现空的目录这是很正常的事情,而且,也是有办法一次性把它们都找出来的。 但是,仅仅列出空目录并不是我们的目的,我们今天了解一下如何删除这些空的目录。 在Linux中查找空目录 查找空目录&#xf…

ThinkPHP5文档学习——配置

文章目录一、配置目录二、配置格式PHP数组定义其它格式的支持二级配置三、配置加载惯例配置应用配置拓展配置场景配置四、读取配置参数五、动态配置设置配置参数六、独立配置独立配置文件V5.0.1版本已经废除该写法自动读取扩展配置七、配置作用域八、环境变量配置一、配置目录 …

语文课内外杂志语文课内外杂志社语文课内外杂志社2022年第14期目录

幼儿教育《语文课内外》投稿:cn7kantougao163.com 家园协同视域下幼儿心理危机的预防与干预对策 曹锭1-3 幼小衔接阶段幼儿时间观念的培养对策 陈晶晶4-6 有效支持 助力幼儿在书海中徜徉 胡玲珊7-9 东西部幼儿园结对帮扶,助力乡村教育扶贫——以广州市人民政府机关幼…

虹科分享|终端安全防护|网络安全术语列表(二)

如果你的工作或者生活与网络安全有关,你就知道它使用了自己独特的、不断发展的语言。术语和缩略语受到网络安全专家的喜爱。因此,我们创建了一个全面的网络安全词汇表,解释了常用的网络安全术语、短语和技术。我们设计此列表是为了揭开安全专…

[附源码]Python计算机毕业设计Django贷款申请审核管理系统论文

项目运行 环境配置: Pychram社区版 python3.7.7 Mysql5.7 HBuilderXlist pipNavicat11Djangonodejs。 项目技术: django python Vue 等等组成,B/S模式 pychram管理等等。 环境需要 1.运行环境:最好是python3.7.7,我…

你知道吗?小程序组件≠小程序插件

一直以为小程序组件和小程序插件是一回事,只是措辞不一样,导致造成乌龙,其实完全是两回事,插件是可以直接提供服务的,组件是给开发者提供的轮子,不能直接提供服务。 先看看微信是如何定义小程序插件的&…

什么是深度卷积可分离Depthwise Separable conv

假设加载进来一张GRB三通道的图片 我现在就把三个通道拆开 注意哦 传统的就是 一个卷积核filter 和三个通道channel 进行卷积 现在这个深度卷积可分离 就要用三个不同的卷积核来对每一个通道进行卷积 小细节 :如果是传统意义上的卷积,但用一个的话&a…

【SQL Server + MySQL一】数据库基本知识、关系数据模型、关系数据语言--关系代数

极其感动!!!当时学数据库的时候,没白学!! 时隔很长时间回去看数据库的笔记都能看懂,每次都靠这份笔记巩固真的是语雀分享要花钱,要不一定把笔记给贴出来(;༎ຶД༎ຶ) ,除…

详解设计模式:装饰器模式

装饰器模式(Decorator Pattern)也称为包装模式(Wrapper Pattern),是 GoF 的 23 种设计模式中的一种结构型设计模式。装饰器模式 是指在不改变原有对象的基础之上,将功能附加到对象上,提供了比继…

3.7.2、IP地址(网际层)

我们日常的大多数网络应用中,属于数据链路层的 MAC 地址,和属于网络层的 IP 地址都在使用,它们之间存在一定的关系。这里主要介绍 IP 地址的作用 1、基本介绍 IP 地址是因特网(Internet)上的主机和路由器所使用的地址,用于标识两…

osgEarth示例分析——osgearth_graticule

前言 本示例最具有借鉴的功能:绘制网格、网格上的文字显示、拾取地球的坐标。在地球网格示例中,可以设置4种网格。执行命令如下: // --geodetic osgearth_graticuled.exe --geodetic earth_image\china-simple.earth // --utm osgearth_gr…

[附源码]Python计算机毕业设计Django大学生心理测评系统

项目运行 环境配置: Pychram社区版 python3.7.7 Mysql5.7 HBuilderXlist pipNavicat11Djangonodejs。 项目技术: django python Vue 等等组成,B/S模式 pychram管理等等。 环境需要 1.运行环境:最好是python3.7.7,我…

磁盘划分和磁盘格式化

文章目录列出装置的 UUID 等参数parted 列出磁盘的分区表类型与分区信息磁盘分区:gdisk、fdisk用 gdisk 新增分区槽用 gdisk 删除一个分区槽磁盘格式化(建立文件系统)XFS 文件系统 mkfs.xfsXFS 文件系统 for RAID 效能优化(Option…

【Linux】环境变量

文章目录一.什么是环境变量二.常见的环境变量1.常见的环境变量和与之相关的命令2.PATH指定命令搜索路径3.env显示所有环境变量4.echo查看环境变量三.本地变量与环境变量的联系四.获取环境变量的三种方式1.getenv()函数获取环境变量2.利用命令行参数3.利用第三方变量一.什么是环…

如何校准Linux服务器时间

最近服务器上的时间与北京时间出现偏差,而Java代码里java.util.Date获取的是服务器时间,导致当前时间错误。 Linux查看服务器当前时间 查看当前时区:date -R 查看当前日期和时间:date 校准Linux服务器时间 Linux中有个ntp包可…

【Unity】URP渲染管线下代码获取相机的Volume Mask属性

步骤1. 引用URP的命名空间2. 获取摄像机3.通过URP扩展获取Volume Mask属性附 层级的相关代码Reference1. 引用URP的命名空间 using UnityEngine.Rendering.Universal;2. 获取摄像机 这里可以使用公有拖拽或者私有赋值的方式。 如果使用拖拽,则: publi…

神经网络和深度学习-多分类问题Softmax Classifier

多分类问题Softmax Classifier 在之前做糖尿病数据集的时候做的二分类问题,因为只有两类,所以只需要输出一个概率值,另一个概率值用1去减去就可以得到 实际上在大多数数据集中是在处理一个多分类问题,例如MNIST中有10类标签 神经…

Hifiasm-meta | 你没看错!基于宏基因组的完成图!!

哈佛大学医学院Dana-Farber癌症研究所李恒课题组重磅推出三代HiFi宏基因组组装软件——hifiasm-meta。研究论文“Metagenome assembly of high-fidelity long reads with hifiasm-meta”预印本在线发布。 宏基因组样本的do novo组装是研究微生物群落的常用方法。与单个物种的组…

RNA-seq 详细教程:分析准备(3)

学习目标 了解 RNA-seq 和差异表达基因的分析流程了解如何设计实验了解如何使用 R 语言进行数据分析1. 简介 在过去的十年中,RNA-seq 已成为转录组差异表达基因和 mRNA 可变剪切分析不可或缺的技术。正确识别哪些基因或转录本在特定条件下的表达情况,是理…