热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

mysql没法用transaction_你应该了解的MySQL锁分类

MySQL中的锁锁是为了解决并发环境下资源竞争的手段,其中乐观并发控制,悲观并发控制和多版本并发控制是数据库并发控制主要采用的技术手段(具体可见我之前的

MySQL中的锁

锁是为了解决并发环境下资源竞争的手段,其中乐观并发控制,悲观并发控制和多版本并发控制是数据库并发控制主要采用的技术手段(具体可见我之前的文章),而MySQL中的锁就是其中的悲观并发控制。

MySQL中的锁有很多种类,我们可以按照下面方式来进行分类。

按读写

从数据库的读写的角度来分,数据库的锁可以分为分为以下几种:

  • 独占锁:又称排它锁、X锁、写锁。X锁不能和其他锁兼容,只要有事务对数据上加了任何锁,其他事务就不能对这些数据再放置X了,同时某个事务放置了X锁之后,其他事务就不能再加其他任何锁了,只有获取排他锁的事务是可以对数据进行读取和修改。
  • 共享锁:又称读锁、S锁。S锁与S锁兼容,可以同时放置。
  • 更新锁:又称U锁。它允许再加S锁,但不允许其他事务再施加U锁或X锁,当被读取的数据要被更新时,则升级S锁为X锁。U锁的优点是允许事务A读取数据的同时不阻塞其它事务,并同时确保事务A自从上次读取数据后数据没有被更改,因此可以减少X锁和S锁的冲突,同时避免使用S锁后再升级为X锁造成的死锁现象。注意,MySQL并不支持U锁,SQLServer才支持U锁。

兼容性矩阵如下(+ 代表兼容, -代表不兼容)

3624633a3067be0229c349c556b04452.png

按粒度

MySQL支持不同级别的锁,其锁定的数据的范围也不同,也即我们常说的锁的粒度。MySQL有三种锁级别:行级锁、页级锁、表级锁。不同的存储引擎支持不同的锁粒度,例如MyISAM和MEMORY存储引擎采用的是表级锁,页级锁仅被BDB存储引擎支持,InnoDB存储引擎支持行级锁和表级锁,默认情况下是采用行级锁。

特点

表级锁:开销小,加锁快;不会出现死锁;锁定粒度大,发生锁冲突的概率最高,并发度最低。数据库引擎总是一次性同时获取所有需要的锁以及总是按相同的顺序获取表锁从而避免死锁。
行级锁:开销大,加锁慢;会出现死锁;锁定粒度最小,发生锁冲突的概率最低,并发度也最高。行锁总是逐步获得的,因此会出现死锁。
页面锁:开销和加锁时间界于表锁和行锁之间;会出现死锁;锁定粒度界于表锁和行锁之间,并发度一般。

下面详细介绍行锁和表锁,页锁由于使用得较少就不介绍了。

行锁

按行对数据进行加锁。InnoDB行锁是通过给索引上的索引项加锁来实现的,Innodb一定存在聚簇索引,行锁最终都会落到聚簇索引上,通过非聚簇索引查询的时候,先锁非聚簇索引,然后再锁聚簇索引。如果一个where语句里面既有聚簇索引,又有二级索引,则会先锁聚簇索引,再锁二级索引。由于是分步加锁的,因此可能会有死锁发生。

MySQL的行锁对S、X锁上做了一些更精确的细分,使得行锁的粒度更细小,可以减少冲突,这就是被称为“precise mode”的兼容矩阵。(该矩阵没有出现在官方文档上,是有人通过Mysql lock0lock.c:lock_rec_has_to_wait源代码推测出来的。)

行锁兼容矩阵
  • 间隙锁(Gap Lock):只锁间隙,前开后开区间(a,b),对索引的间隙加锁,防止其他事务插入数据。
  • 记录锁(Record Lock):只锁记录,特定几行记录。
  • 临键锁(Next-Key Lock):同时锁住记录和间隙,前开后闭区间(a,b]。
  • 插入意图锁(Insert Intention Lock):插入时使用的锁。在代码中,插入意图锁,实际上是GAP锁上加了一个LOCK_INSERT_INTENTION的标记。

右侧是已加的锁

28e3b487713b1a512ff43c3afd7389e4.png

S锁和S锁是完全兼容的,因此在判别兼容性时不需要对比精确模式。精确模式的检测,用在S、X和X、X之间。从这个矩阵可以看到几个特点:

  • INSERT操作之间不会有冲突:你插入你的,我插入我的。
  • GAP,Next-Key会阻止Insert:插入的数据正好在区间内,不允许插入。
  • GAP和Record,Next-Key不会冲突
  • Record和Record、Next-Key之间相互冲突。
  • 已有的Insert锁不阻止任何准备加的锁。
  • 间隙锁(无论是S还是X)只会阻塞insert操作。

注意点
  • 对于记录锁,列必须是唯一索引列或者主键列,查询语句必须为精确匹配,如“=”,否则记录锁会退化为临键锁。
  • 间隙锁和临键锁基于非唯一索引,在唯一索引列上不存在间隙锁和临键锁。

表锁与锁表的误区

只有正确通过索引条件检索数据(没有索引失效的情况),InnoDB才会使用行级锁,否则InnoDB对表中的所有记录加锁,也就是将锁住整个表。注意,这里说的是锁住整个表,但是Innodb并不是使用表锁来锁住表的,而是使用了下面介绍的Next-Key Lock来锁住整个表。网上很多的说法都是说用表锁,然而实际上并不是,我们可以通过下面的例子来看看。

假设我们有以下的数据(MySQL8):

mysql> select * from users;+----+------+-----+| id | name | age |+----+------+-----+| 1 | a | 1 || 2 | a | 1 || 3 | a | 1 || 4 | a | 1 || 5 | a | 1 |+----+------+-----+

方法一:

我们使用表锁锁表,并查看引擎的状态

mysql> begin;Query OK, 0 rows affected (0.00 sec)mysql> lock tables users write;Query OK, 0 rows affected (0.00 sec)mysql> show engine innodb statusG...------------TRANSACTIONS------------Trx id counter 4863Purge done for trx&#39;s n:o <4862 undo n:o <0 state: running but idleHistory list length 911LIST OF TRANSACTIONS FOR EACH SESSION:---TRANSACTION 281479760456232, not startedmysql tables in use 1, locked 1 ###############注意这里0 lock struct(s), heap size 1136, 0 row lock(s)...

然后我们再通过非索引的字段查询来加锁&#xff0c;并查看引擎的状态

## 先解锁上次的表锁mysql> unlock tables;Query OK, 0 rows affected (0.00 sec)mysql> commit;Query OK, 0 rows affected (0.00 sec)mysql> begin;Query OK, 0 rows affected (0.00 sec)mysql> select * from users where name &#61; &#39;a&#39; for update;mysql> show engine innodb statusG...------------TRANSACTIONS------------Trx id counter 4864Purge done for trx&#39;s n:o <4862 undo n:o <0 state: running but idleHistory list length 911LIST OF TRANSACTIONS FOR EACH SESSION:---TRANSACTION 4863, ACTIVE 37 sec2 lock struct(s), heap size 1136, 6 row lock(s) ###############注意这里...

然后我们再删除id为2&#xff0c;3&#xff0c;4的数据&#xff0c;然后在通过非索引的字段查询来加锁&#xff0c;并查看引擎的状态

mysql> delete from users where id in (2,3,4);Query OK, 3 rows affected (0.00 sec)mysql> commit;Query OK, 0 rows affected (0.00 sec)mysql> begin;Query OK, 0 rows affected (0.00 sec)mysql> select * from users where name &#61; &#39;a&#39; for update;mysql> show engine innodb statusG...------------TRANSACTIONS------------Trx id counter 4870Purge done for trx&#39;s n:o <4869 undo n:o <0 state: running but idleHistory list length 914LIST OF TRANSACTIONS FOR EACH SESSION:---TRANSACTION 4869, ACTIVE 9 sec2 lock struct(s), heap size 1136, 3 row lock(s) ###############注意这里...

可以看到这里使用了表锁和因为没法用索引锁定特定行而转而锁住整个表是不一样的。从第二次和第三次的操作来看&#xff0c;lock住的row也是不同的&#xff0c;这是因为两者间隙的个数不同&#xff0c;所以可以看到使用的并不是表锁&#xff0c;而是Next-Key Lock。第一次锁住了(-∞,1],(1,2],(2,3],(3,4],(4,5],(5,∞]&#xff0c;第二次锁住了(-∞,1],(1,5],(5,∞]。

方法二&#xff1a;

也可以通过以下语句来查看锁的信息&#xff0c;也可以知道用的是行锁&#xff0c;且是锁住了区间(插入不了数据)和记录&#xff0c;所以是Next-Key Lock。

mysql> select ENGINE_TRANSACTION_ID,LOCK_TYPE,LOCK_MODE from performance_schema.data_locks where ENGINE_TRANSACTION_ID in (你的事务id);&#43;-----------------------&#43;-----------&#43;-----------&#43;| ENGINE_TRANSACTION_ID | LOCK_TYPE | LOCK_MODE |&#43;-----------------------&#43;-----------&#43;-----------&#43;| 4889 | TABLE | IX || 4889 | RECORD | X || 4889 | RECORD | X || 4889 | RECORD | X |&#43;-----------------------&#43;-----------&#43;-----------&#43;10 rows in set (0.00 sec)

LOCK_TYPE&#xff1a;对于InnoDB&#xff0c;可选值为 RECORD(行锁)&#xff0c; TABLE(表锁)

LOCK_MODE&#xff1a;对于InnoDB&#xff0c;可选值为S[,GAP], X[,GAP], IS[,GAP],IX[,GAP], AUTO_INC和UNKNOWN。除了AUTO_INC和UNKNOWN&#xff0c;其他锁定模式都包含了GAP锁(如果存在)。

具体可见 MySQL文档&#xff1a;https://dev.mysql.com/doc/refman/8.0/en/performance-schema-data-locks-table.html

表级锁

直接对整个表加锁&#xff0c;影响表中所有记录&#xff0c;表读锁和表写锁的兼容性见上面的分析。

MySQL中除了表读锁和表写锁之外&#xff0c;还存在一种特殊的表锁&#xff1a;意向锁&#xff0c;这是为了解决不同粒度的锁的兼容性判断而存在的。

意向锁

因为锁的粒度不同&#xff0c;表锁的范围覆盖了行锁的范围&#xff0c;所以表锁和行锁会产生冲突&#xff0c;例如事务A对表中某一行数据加了行锁&#xff0c;然后事务B想加表锁&#xff0c;正常来说是应该要冲突的。如果只有行锁的话&#xff0c;要判断是否冲突就得遍历每一行数据了&#xff0c;这样的效率实在不高&#xff0c;因此我们就有了意向表锁。

意向锁的主要目的是为了使得 行锁表锁 共存&#xff0c;事务在申请行锁前&#xff0c;必须先申请表的意向锁&#xff0c;成功后再申请行锁。注意&#xff1a;申请意向锁的动作是数据库完成的&#xff0c;不需要开发者来申请。

意向锁是表级锁&#xff0c;但是却表示事务正在读或写某一行记录&#xff0c;而不是整个表&#xff0c; 所以意向锁之间不会产生冲突&#xff0c;真正的冲突在加行锁时检查。

意向锁分为意向读锁(IS)和意向写锁(IX)。

表锁的兼容性矩阵
cbc813ff10a9c834f985adf7f851c5fa.png

作者: X先生
https://segmentfault.com/a/1190000023869573




推荐阅读
  • 本文详细介绍了 PHP 中对象的生命周期、内存管理和魔术方法的使用,包括对象的自动销毁、析构函数的作用以及各种魔术方法的具体应用场景。 ... [详细]
  • MySQL Decimal 类型的最大值解析及其在数据处理中的应用艺术
    在关系型数据库中,表的设计与SQL语句的编写对性能的影响至关重要,甚至可占到90%以上。本文将重点探讨MySQL中Decimal类型的最大值及其在数据处理中的应用技巧,通过实例分析和优化建议,帮助读者深入理解并掌握这一重要知识点。 ... [详细]
  • PTArchiver工作原理详解与应用分析
    PTArchiver工作原理及其应用分析本文详细解析了PTArchiver的工作机制,探讨了其在数据归档和管理中的应用。PTArchiver通过高效的压缩算法和灵活的存储策略,实现了对大规模数据的高效管理和长期保存。文章还介绍了其在企业级数据备份、历史数据迁移等场景中的实际应用案例,为用户提供了实用的操作建议和技术支持。 ... [详细]
  • 本文详细介绍了MySQL数据库的基础语法与核心操作,涵盖从基础概念到具体应用的多个方面。首先,文章从基础知识入手,逐步深入到创建和修改数据表的操作。接着,详细讲解了如何进行数据的插入、更新与删除。在查询部分,不仅介绍了DISTINCT和LIMIT的使用方法,还探讨了排序、过滤和通配符的应用。此外,文章还涵盖了计算字段以及多种函数的使用,包括文本处理、日期和时间处理及数值处理等。通过这些内容,读者可以全面掌握MySQL数据库的核心操作技巧。 ... [详细]
  • 在关系型数据库中,数据约束是指在向数据表中插入数据时必须遵循的限制条件。在MySQL和MariaDB中,常见的数据约束包括主键约束、唯一键约束、外键约束以及非空约束等。这些约束确保了数据的完整性和一致性,是数据库管理中的重要组成部分。通过合理设置和使用这些约束,可以有效防止数据冗余和错误,提升数据库的可靠性和性能。 ... [详细]
  • 针对MySQL Undo空间满载及Oracle Undo表空间溢出的问题,本文详细探讨了其原因与解决策略。首先,通过启动SQL*Plus并以SYS用户身份登录数据库,查询当前数据库的UNDO表空间名称,确认当前状态。接着,分析导致Undo空间满载的常见原因,如长时间运行的事务、频繁的更新操作等,并提出相应的解决方案,包括调整Undo表空间大小、优化事务管理、定期清理历史数据等。最后,结合实际案例,提供具体的实施步骤和注意事项,帮助DBA有效应对这些问题。 ... [详细]
  • 本文详细介绍了使用 Python 进行 MySQL 和 Redis 数据库操作的实战技巧。首先,针对 MySQL 数据库,通过 `pymysql` 模块展示了如何连接和操作数据库,包括建立连接、执行查询和更新等常见操作。接着,文章深入探讨了 Redis 的基本命令和高级功能,如键值存储、列表操作和事务处理。此外,还提供了多个实际案例,帮助读者更好地理解和应用这些技术。 ... [详细]
  • 如何精通编程语言:全面指南与实用技巧
    如何精通编程语言:全面指南与实用技巧 ... [详细]
  • 2012年9月12日优酷土豆校园招聘笔试题目解析与备考指南
    2012年9月12日,优酷土豆校园招聘笔试题目解析与备考指南。在选择题部分,有一道题目涉及中国人的血型分布情况,具体为A型30%、B型20%、O型40%、AB型10%。若需确保在随机选取的样本中,至少有一人为B型血的概率不低于90%,则需要选取的最少人数是多少?该问题不仅考察了概率统计的基本知识,还要求考生具备一定的逻辑推理能力。 ... [详细]
  • 如何使用mysql_nd:Python连接MySQL数据库的优雅指南
    无论是进行机器学习、Web开发还是爬虫项目,数据库操作都是必不可少的一环。本文将详细介绍如何使用Python通过 `mysql_nd` 库与 MySQL 数据库进行高效连接和数据交互。内容涵盖以下几个方面: ... [详细]
  • 在过去,我曾使用过自建MySQL服务器中的MyISAM和InnoDB存储引擎(也曾尝试过Memory引擎)。今年初,我开始转向阿里云的关系型数据库服务,并深入研究了其高效的压缩存储引擎TokuDB。TokuDB在数据压缩和处理大规模数据集方面表现出色,显著提升了存储效率和查询性能。通过实际应用,我发现TokuDB不仅能够有效减少存储成本,还能显著提高数据处理速度,特别适用于高并发和大数据量的场景。 ... [详细]
  • 深入解析 SQL 数据库查询技术
    本文深入探讨了SQL数据库查询技术,重点讲解了单表查询的各种方法。首先,介绍了如何从表中选择特定的列,包括查询指定列、查询所有列以及计算值的查询。此外,还详细解释了如何使用列别名来修改查询结果的列标题,并介绍了更名运算的应用场景和实现方式。通过这些内容,读者可以更好地理解和掌握SQL查询的基本技巧和高级用法。 ... [详细]
  • Presto:高效即席查询引擎的深度解析与应用
    本文深入解析了Presto这一高效的即席查询引擎,详细探讨了其架构设计及其优缺点。Presto通过内存到内存的数据处理方式,显著提升了查询性能,相比传统的MapReduce查询,不仅减少了数据传输的延迟,还提高了查询的准确性和效率。然而,Presto在大规模数据处理和容错机制方面仍存在一定的局限性。本文还介绍了Presto在实际应用中的多种场景,展示了其在大数据分析领域的强大潜力。 ... [详细]
  • 在使用SSH框架进行项目开发时,经常会遇到一些常见的问题。例如,在Spring配置文件中配置AOP事务声明后,进行单元测试时可能会出现“No Hibernate Session bound to thread”的错误。本文将详细探讨这一问题的原因,并提供有效的解决方案,帮助开发者顺利解决此类问题。 ... [详细]
  • 在数据库事务处理中,InnoDB 存储引擎提供了多种隔离级别,其中 READ COMMITTED 和 REPEATABLE READ 是两个常用的选项。本文详细对比了这两种隔离级别的特点和差异,不仅从理论角度分析了它们对“脏读”和“幻读”的处理方式,还结合实际应用场景探讨了它们在并发控制和性能表现上的不同。特别关注了行锁机制在不同隔离级别下的行为,为开发者选择合适的隔离级别提供了参考。 ... [详细]
author-avatar
七彩咩_131
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有