Mysql(15)——锁机制 + MVCC(全)

2023-05-16

前言

事务的隔离级别在之前我们已经学习过,那么事务隔离级别的实现原理是什么呢?锁+MVCC
在这里插入图片描述

下面我们就来分开讲解:

表级锁&行级锁

注意:表锁和行锁说的是锁的粒度,不要以为它与下面讲到的其他锁是单独的概念。因为有表级共享锁等概念的存在。

  • 表级锁:对整张表加锁。开销小,加锁快,不会出现死锁;锁粒度大,发生锁冲突的概率高,并发度低。
  • 行级锁:对某行记录加锁。开销大,加锁慢,会出现死锁;锁定粒度最小,发生锁冲突的概率最低,并发度高。

排它锁&共享锁

  • 排它锁(Exclusive),又称为X 锁,锁。
  • 共享锁(Shared),又称为S 锁,锁。

X和S锁之间有以下的关系:
SS可以兼容的,XS、SX、XX之间是互斥的

  • 一个事务对数据对象 O 加了 S 锁,可以对 O 进行读取操作但不能进行更新操作。加锁期间其它事务能对O 加 S 锁但不能加 X 锁。
  • 一个事务对数据对象 O 加了 X 锁,就可以对 O 进行读取和更新。加锁期间其它事务不能对 O 加任何锁。

显示加锁:

  • select ... lock in share mode 强制获取共享锁,
  • select ... for update 强制获取排它锁

在这里插入图片描述
从上面的示例中,我们可以看出InnoDB中,是支持行锁的。
以主键为过滤条件时,事务1和事务2可以获取不同行的排它锁

接着看以下的例子,以非索引列作为过滤条件,会出现什么情况呢?
在这里插入图片描述
于是我们得出结论:
InnoDB的行锁是加在索引项上面的,是给索引加锁,并不是单纯的给行记录加锁,所以如果过滤条件没有加索引的话,使用的就是表锁,而不是行锁。 因此,我们得出InnoDB并不是只有行锁,还存在表锁。

行锁使用的条件:加在索引项上。

InnoDB行级锁

1、行级锁

InnoDB存储引擎支持事务处理,表支持行级锁定,并发能力更好。

  1. InnoDB行锁是通过给索引上的索引项加锁来实现的,而不是给表的行记录加锁实现的,这就意味着只有通过索引条件检索数据,InnoDB才使用行级锁,否则InnoDB将使用表锁。 (原理是在二级索引树上搜索了主键索引)
  2. 由于InnoDB的行锁实现是针对索引字段添加的锁,不是针对行记录加的锁,因此虽然访问的是InnoDB引擎下表的不同行,但是如果使用相同的索引字段作为过滤条件,依然会发生锁冲突,只能串行进行,不能并发进行。
  3. 即使SQL中使用了索引,但是经过MySQL的优化器后,如果认为全表扫描比使用索引效率更高,此时会放弃使用索引,因此也不会使用行锁,而是使用表锁(此时需要注意间隙锁与表锁),比如对一些很小的表,MySQL就不会去使用索引。

所以:InnoDB默认情况下是使用表锁,一旦使用索引项,就会使用行锁。

串行化隔离级别的实现原理

串行化的隔离级别之下,不需要手动获取锁,会自动加上表的共享锁和排它锁 , 示例如下:
在这里插入图片描述
我们知道串行化的隔离级别之下,解决了可重复读未解决的幻读问题,那么它是怎么实现的呢?这就是我们接下来要学习的内容。

串行化解决幻读:依靠的就是间隙锁。

2、 间隙锁 : 串行化隔离级别下解决幻读的利器

当我们用范围条件而不是相等条件检索数据,并请求共享或排他锁时,InnoDB 会给符合条件的已有数据记录的索引项加锁;对于键值在条件范围内但并不存在的记录,叫做“间隙(GAP)” ,InnoDB 也会对这个“间隙”加锁,这种锁机制就是所谓的间隙锁。

在这里插入图片描述
不仅给表中满足范围查询要求的记录间隙加锁,给在其之后的 “ 空洞 ” 区域也加上了锁。
间隙(gap)(左开右闭)的范围是 : (11,12],(12,22],(22,23],(23,+正无穷)
给这些间隙加锁,就叫做间隙锁。

在串行化的隔离级别下:使用范围查询不仅给满足范围的记录添加了行锁,还添加了间隙锁,从而消除幻读现象。
next-key lock : record-lock + gap-lock

请添加图片描述

读者在理解时要注意思考间隙锁的作用范围。
当查询的表是小表时,很可能用不到索引,此时在串行化的模式下就会添加表锁,即使你插入的范围不再间隙锁的范围之内,也无法插入。

综上,这种情况解决了串行化在范围查询下的幻读问题

那么在等值查询的条件下,间隙锁是如何工作的呢?

  • 在等值查询时,使用的过滤条件如果是主键索引或者唯一键索引,由于值不可以重复,添加行锁后,那么在另一个事务中也是可以完成插入操作的。
  • 在等值查询时,使用的过滤条件如果是辅助索引,由于值是可以重复的,为了防止幻读,就会在已过滤的辅助索引列上加上行锁之后,再在其左右两边范围内加上间隙锁,在间隙锁范围内是不能够完成插入获取排它锁。

示例如下:
在这里插入图片描述

InnoDb表级锁

在绝大部分情况下都应该使用行锁,因为事务和行锁往往是选择InnoDb的理由,但个别情况下也会使用表锁:

  1. 事务需要更新大部分或全部数据,表又比较大,如果使用默认的行锁,不仅仅这个事务的执行效率低,而且会造成其他事物长时间等待和锁冲突。
  2. 事务涉及多个表,比较复杂,很容易引起死锁,造成大量事务回滚。
LOCK TABLE user READ;读锁锁表
LOCK TABLE user WRITE; 写锁锁表
事务执行...
COMMIT/ROLLBACK; 事务提交或者回滚
UNLOCK TABLES; 本身自带提交事务,释放线程占用的所有表锁

在了解了串行化是如何解决幻读之后,那么已提交读和可重复读的隔离级别下的并发控制是如何实现的呢?
——底层实现原理:MVCC(多版本并发控制):并发读取方式:快照读

已提交读和可重复读的底层原理

InnoDb提供了两种读取操作,锁定读(s和x)和非锁定读(MVCC提供的快照读),依赖底层的undolog(回滚日志)

追根溯源,我们一步一步来看:

事务日志:uodo log和redo log

  1. ACD(事务日志)
  2. I(锁【串行化】+ MVCC【已提交读和可重复读】)

undo log的主要作用是:

  1. 事务发生错误时,回滚日志
  2. 提供了MVCC的非锁定读(快照读)的关键技术

undo log的原理

在这里插入图片描述

MVCC的多版本并发控制

  • MVCC是多版本并发控制,(Multi-Version Concurrency Control)是Mysql基于乐观锁理论实现隔离级别的方式,用于实现已提交读和可重复读隔离级别的实现,也经常称为多版本数据库。
  • MVCC机制会生成一个数据请求时间点的一致性数据快照,并用这个快照提供一定级别(语句级或事务级)的一致性读取,好像数据库可以提供同一数据的多个版本(系统版本号和事务版本号)

MVCC的多版本并发控制中,读操作分为以下的两类:

  1. 快照读:读的是记录的可见版本,不用加锁,如select;
  2. 当前读:读取的是记录的最新版本,如insert,delete,update,select …lock in share mode/for update、

快照内容读取原则:(仔细理解,跟下面的内容息息相关)
1、版本未提交无法读取生成快照
2、版本已提交,但是在快照创建后提交的,无法读取
3、版本已提交,但是在快照创建前提交的,可以读取
4、当前事务内自己的更新,可以读到

1、已提交读解决脏读的原理以及为什么没有解决不可重复读和幻读的原因:

  • 每次select的时候会重新生成一次快照(前提是数据已经被另一个事务正确commit过了,此时脏读时数据是处于prepare状态,此时事务生成的快照还是旧数据生成的快照,并没有用未提交的数据照快照),另一个事务一旦提交,此时再次select快照,照到的就是已提交的新数据。
  • 由于每一次select都会重新产生一次数据快照,其他事务更新后而且已提交的数据实时反馈到当前事务的select结果中,因此已提交读并没有解决不可重复读的问题。
  • 由于每一次select都会重新产生一次数据快照,其他事务增加了和当前事务查询条件相同的新数据时并且已经成功commit,导致当前事务再次以同样的条件查询时,数据变多了。
    在这里插入图片描述

2、可重复读解决脏读和不可重复读的原理以及为什么部分解决幻读的原因

  • 解决脏读的原理和之前相同
  • 解决了不可重复读的原因:因为第一次select产生数据快照,而且只产生一次!其他事务虽然修改了最新的数据,但是当前事务select时依然查看的是最初的快照数据。

在这里插入图片描述

  • 部分解决了幻读问题的原因:

①因为第一次select产生数据快照,而且只产生一次!其他事务虽然新增了数据,但是当前事务select时依然查看的是最初的快照数据。
在这里插入图片描述

②当前事务是可以看见自己事务修改、更新的数据的(此时事务ID号会更新,旧数据会到lundo log中),此时不是快照读,而是当前读,再去select,此时就没有解决幻读问题:
在这里插入图片描述

意向共享锁和意向排它锁

注意:这两个概念都是和表锁相关的,存在的目的就是更快速地获取表锁。InnoDb行锁的存在可能会导致获取表锁时效率较低,为了避免大面积的行锁扫描,我们直接通过观察表级的意向锁即可,就可以判断能否获取当前表的X或S锁。

在这里插入图片描述

  • 意向共享锁(IS锁):事务计划给记录加行共享锁,事务在给一行记录加共享锁前,必须先取得该表的IS 锁。
  • 意向排他锁(IX锁):事务计划给记录加行排他锁,事务在给一行记录加排他锁前,必须先取得该表的IX 锁。

请添加图片描述

1、意向锁是由InnoDB存储引擎获取行锁之前自己获取的
2、意向锁之间都是兼容的,不会产生冲突
3、意向锁存在的意义是为了更高效的获取表锁(表格中的X和S指的是表锁,不是行锁!!!
4、意向锁是表级锁,协调表锁和行锁的共存关系。主要目的是显示事务正在锁定某行或者试图锁定某行。

死锁

MyISAM 表锁是 deadlock free 的, 这是因为 MyISAM 总是一次获得所需的全部锁,要么全部满足,要么等待,因此不会出现死锁。但在 InnoDB 中,除单个 SQL 组成的事务外,锁是逐步获得的,即锁的粒度比较小,这就决定了在 InnoDB 中发生死锁是可能的。

请添加图片描述
在这里插入图片描述
死锁问题一般都是我们自己的应用造成的,和多线程编程的死锁情况相似,大部分都是由于我们多个线程在获取多个锁资源的时候,获取的顺序不同而导致的死锁问题。因此我们应用在对数据库的多个表做更新的时候,不同的代码段,应对这些表按相同的顺序进行更新操作,以防止锁冲突导致死锁问题。

锁的优化建议

1.尽量使用较低的隔离级别
2.设计合理的索引并尽量使用索引访问数据,使加锁更加准确,减少锁冲突的机会提高并发能力
3.选择合理的事务大小,小事务发生锁冲突的概率小
4.不同的程序访问一组表时,应尽量约定以相同的顺序访问各表,对一个表而言,尽可能以固定的顺序存取表中的行。这样可以大大减少死锁的机会
5.尽量用相等条件访问数据,这样可以避免间隙锁对并发插入的影响(等值查询与范围查询)
6.不要申请超过实际需要的锁级别
7.除非必须,查询时不要显示加锁

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Mysql(15)——锁机制 + MVCC(全) 的相关文章

  • 如何使用Query备份MySQL数据库?

    我们如何使用 Query 备份 MySQL 数据库 就像我们可以使用以下 Query 备份 MS SQL 一样 Query backup database DATABASENAME to disk PATH 使用 mysqldump php
  • mysql REGEXP 不匹配

    我有一个正则表达式 旨在捕获字符串中的电话号码 1 s d 3 s d 3 s d 4 我尝试使用以下查询在 MySql 数据库中查询此正则表达式 SELECT FROM everything instances meta AS m WHE
  • MySQL:错误 1215 (HY000):无法添加外键约束

    我读过了数据库系统概念 第六版 西尔伯沙茨 我将在 OS X 上的 MySQL 上实现第 2 章中所示的大学数据库系统 但我在创建表格时遇到了麻烦course 桌子department好像 mysql gt select from depa
  • 根据另一个表中的值查找总计数

    在Mysql中 我的表中有具有重复值的城市 表城市 Name New York USA New York USA Chicago USA Chicago USA Chicago USA Paris France Nice France Mi
  • mysql中更新查询中的多个set和where子句

    我认为这是不可能的 因为我找不到任何东西 但我想我会在这里检查一下 以防我没有寻找正确的东西 我的数据库中有一个设置表 其中有两列 第一列是设置名称 第二列是值 我需要同时更新所有这些 我想看看是否有一种方法可以在一个查询的同时更新这些值
  • 如何在mysql工作台中打开多个模型/数据库

    我有两个型号 1 Server Model conneted to remote database which is stored on server 2 Local Host connected to my pc database is
  • 如何在应用程序级别管理只读数据库连接

    我们使用的是Java Spring Ibatis MySql 有没有办法利用这些技术在应用程序级别管理只读连接 我希望在只读 MySql 用户的基础上添加额外的保护层 如果 BasicDataSource 或 SqlMapClientTem
  • Perl:通过一次 MySQL 调用更新多行

    似乎这不可能 但嘿我不妨问一下 我可能是错的 想知道 perl 是否可以使用一个 MySQL 调用来更新多行 我正在使用 DBI 任何帮助或反馈将不胜感激 这可以通过 ASP 和 ASP net 在 MSSQL 中实现 所以想知道是否也可以
  • 将数据导入 MySQL Workbench

    我有一个包含 6 个表的数据库 我想将这些表导入到existingMySQL Workbench 中的数据库 我使用 phpMyAdmin 将数据库导出到 sql文件 并使用 数据导入 恢复 按钮将其导入到 MySQL Workbench
  • .NET、C#、LINQ、SQL 和 OR 映射 - 我只是不明白:(

    我只是不明白 我什至不确定我是否在寻找正确的方向 问题 这就是我的 C 应用程序 我通过 SSH 连接到在线 MySQL 数据库 现在我可以使用 MySQL Connector Net 驱动程序 http dev mysql com dow
  • 我的 mac 上的 python mysqldb 错误:库未加载:@rpath/libmysqlclient.21.dylib

    import MySQLdb leads to Traceback most recent call last File
  • 如何在MySQL中使用数字字符串的比较运算符?

    我有一个员工表 有类似领域的经验VARCHAR类型 此字段结合了用短划线 分隔的总年份和总月份 因此我必须按年份过滤具有 3 年以上经验的经验 我的表结构 所以现在我必须获得3年以上经验的id 我尝试如下 SELECT FROM emplo
  • mysql 时钟

    我有一个包含以下元素的时钟表 id pk action emp id fk time 如果我通过了 如何选择最新的动作emp id到查询 id emp id action current time 1 1 clockin 2012 01 2
  • Sqoop mysql错误-通信链路故障

    尝试运行以下命令 sqoop import connect jdbc mysql 3306 home credit risk table bureau target dir home sqoop username root password
  • 当 mysql_connect 不适用于 IIS 上的 PHP 时,不会返回任何错误消息

    我是 PHP 和 MySQL 的新手 最近在已经运行 IIS v6 的 Windows Server 2003 服务器上安装了 PHP v5 3 10 和 MySQL v 5 5 21 PHP 运行 我已经从 MySQL 5 5 命令行客户
  • 进行 URL 重写

    当我点击网站上给定条目的评论部分时 URL 如下所示 http www com comments index php submission Portugal 20Crushes 20North 20Korea submissionid 62
  • PHP mysql 土耳其语字符编码及比较

    我正在尝试通过 AJAX POST 从 MySql 数据库中过滤土耳其语姓名 英文字母单词列出一切正常 但是如果我发送 这是带点的字母 O 结果不仅是 还包括 O 和 另外我注意到 AJAX 帖子被发送 作为 C3 96 有人可以帮忙吗 请
  • 从 Inno Setup 项目内部调用 MySQL

    我正在为一些使用 MySQL 的软件编写安装程序 我正在尝试运行 sql用于在安装时设置数据库的脚本 唉 我目前在执行它时遇到了很大的问题 这个问题似乎是由于这样一个事实而产生的 当你设置一条通往 sql文件内的 execute SOURC
  • 如何提取 MySQL 日期中的月份和年份并进行比较?

    如何从 mySQL 日期中提取月份和日期并将其与另一个日期进行比较 我找到了 this MONTH 但它只获取月份 我寻找月份和年份 在Mysql Doku中 http dev mysql com doc refman 5 5 en dat
  • 如何提高MySQL INSERT和UPDATE性能?

    我们数据库中的 INSERT 和 UPDATE 语句的性能似乎正在下降 并导致我们的 Web 应用程序性能不佳 表是InnoDB 应用程序使用事务 我可以做一些简单的调整来加快速度吗 我认为我们可能会遇到一些锁定问题 我怎样才能找到答案 你

随机推荐