我的编程空间,编程开发者的网络收藏夹
学习永远不晚

MySQL&InnoDB锁机制全面解析

短信预约 信息系统项目管理师 报名、考试、查分时间动态提醒
省份

北京

  • 北京
  • 上海
  • 天津
  • 重庆
  • 河北
  • 山东
  • 辽宁
  • 黑龙江
  • 吉林
  • 甘肃
  • 青海
  • 河南
  • 江苏
  • 湖北
  • 湖南
  • 江西
  • 浙江
  • 广东
  • 云南
  • 福建
  • 海南
  • 山西
  • 四川
  • 陕西
  • 贵州
  • 安徽
  • 广西
  • 内蒙
  • 西藏
  • 新疆
  • 宁夏
  • 兵团
手机号立即预约

请填写图片验证码后获取短信验证码

看不清楚,换张图片

免费获取短信验证码

MySQL&InnoDB锁机制全面解析

MySQL&InnoDB锁机制全面解析

本文系统性介绍了MySQL&InnoDB的锁机制。

 

目录

    • 一、前言
    • 二、锁的类型
      • 2.1 全局锁
      • 2.2 表级锁
        • 2.2.1 表锁
        • 2.2.2 元数据锁(Meta Data Locks)
        • 2.2.3 自增列锁(AUTO-INC Locks)
        • 2.2.4 意向锁 (Intention Locks)
      • 2.3 行级锁
        • 2.3.1 Record Locks
        • 2.3.2 Gap Locks
        • 2.3.3 Next-Key Locks
        • 2.3.4 插入意向锁(Insert Intention Locks )
    • 三、死锁
    • 四、小结

 

一、前言

数据库使用锁是为了支持对共享资源的并发访问,同时保证数据的完整性和一致性。其中,MySQL在Server层和InnoDB引擎设计了多种类型的锁机制,用于实现不同场景下的并发控制,下面我们分析一下这些锁的定义和使用场景。

二、锁的类型

作用范围划分

  • 全局锁
    1. FTWRL(Flush tables with read lock)
  • 表级锁
    1. 元数据锁MDL(meta data lock)
    2. 表锁
    3. 意向锁
    4. AUTO-INC Locks
  • 行级锁
    1. Record Locks
    2. Gap Locks
    3. Next-Key Locks
    4. Insert Intention Locks

权限互斥划分

  • 共享锁
    1. 意向共享锁IS
    2. 表共享锁
    3. 行共享锁
  • 排它锁
    1. 意向排它锁IX
    2. 表排它锁
    3. 行排它锁

 

2.1 全局锁

 

FLUSH TABLES WITH READ LOCK: Closes all open tables and locks all tables for all databases with a global read lock.
This operation is a very convenient way to get backups if you have a file system such as Veritas or ZFS that can take snapshots in time. Use UNLOCK TABLES to release the lock.

全局锁意味着对整个数据库实例加上锁。通常使用的是全局读锁——Flush tables with read lock (FTWRL)。
使用这个命令,可以使整个库处于只读状态,其他的线程无论使用DML、DDL甚至是事务的提交语句都会无法正常执行。
在这里插入图片描述
使用场景

做全库逻辑备份,对所有的表数据进行锁定,保证数据的一致性。

问题

但是在进行备份时使用FTWRL的全局锁方案有比较严重的缺陷:

  • 如果是在主库上进行备份,整个备份期间主库都不能执行任何数据更新操作,业务无法正常进行,这是不可接受的;
  • 如果是在从库上进行备份,整个备份期间从库都不能执行主库同步过来的 binlog,会直接导致主从延迟。

这个方案一般会使用在MyISAM 这种不支持事务的引擎,而对于InnodDB来说,可以在主从备份时使用mysqldump 参数**–single-transaction**开启一个事务,利用MVCC的特性,拿到一致性视图数据,保证数据的一致性和业务正常运行。

2.2 表级锁

2.2.1 表锁

表锁通常指的是表级别的S锁和X锁,命令是 lock tables … read/write。 当使用lock tables … read时,任何线程对该表进行DDL和DML都会失败;使用lock tables … write时,只允许当前持有表锁的线程才能读和写该表。
在这里插入图片描述
在这里插入图片描述

对于支持行锁的InnoDB引擎来说,一般不会使用表级别的S锁和X锁,因此显得比较“鸡肋”。
而实际项目过程中,经常会有这样的场景,在对一个表进行DDL表结构变更时,对表记录的增删改查操作会被阻塞;反之对表数据进行增删改查时,也不允许执行表结构变更,如果不使用表锁怎么实现呢?答案是:通过元数据锁进行控制。

 

2.2.2 元数据锁(Meta Data Locks)

 

MySQL uses metadata locking to manage concurrent access to database objects and to ensure data consistency. Metadata locking applies not just to tables, but also to schemas and stored programs (procedures, functions, triggers, and scheduled events).

Meta Data Lock 简称MDL,是在MySQL server层使用的一种表级别锁,并不是InnoDB引擎中实现的。使用时不需要显式声明

  • 当对表进行增删改查操作的时候,会自动加 MDL 读锁;
  • 当要对表做结构变更操作的时候,会自动加 MDL 写锁。

读读共享,因此可以同时对一张表进行增删改查;读写互斥,写写互斥,多个线程同时修改表结构时,需要排队等待执行。保证表结构变更操作的安全性。

元数据锁的兼容关系如下:

兼容性MDL 读锁MDL 写锁
MDL 读锁 兼容 不兼容
MDL 写锁 不兼容 不兼容

 

2.2.3 自增列锁(AUTO-INC Locks)

 

An AUTO-INC lock is a special table-level lock taken by transactions inserting into tables with AUTO_INCREMENT columns. In the simplest case, if one transaction is inserting values into the table, any other transactions must wait to do their own inserts into that table, so that rows inserted by the first transaction receive consecutive primary key values.

AUTO-INC锁是一种特殊的表级锁,当表使用了AUTO_INCREMENT列时,插入数据时需要获取AUTO-INC锁。AUTO-INC锁是作用范围是语句级别,也就是说当执行完成插入语句后,哪怕整个事务还没结束,AUTO-INC锁也会被释放。因此会出现:一个事务在持有AUTO-INC锁进行插入操作时,其他事务的插入操作就会被阻塞,以此来保证自增值是连续的。

问题

使用AUTO-INC Locks会出现这样的问题:如果一个插入语句执行过长(比如insert … select大数据量插入),会导致后面的插入语句阻塞时间久,整体性能降低

解决方案

所以MySQL InnoDB引擎还会采用另一种轻量级锁(互斥量)的方式,在执行插入语句之前先获取该轻量级锁,生成AUTO_INCREMENT的值后就释放锁,不需要等到插入语句执行完成后才释放。这种方式会大大提高AUTO_INCREMENT值插入的性能,但是也会带来的问题是——并发时事务的自增列值是不连续的,主从复制时可能是不安全的

使用innodb_autoinc_lock_mode系统变量可以控制选择哪一种锁来为AUTO_INCREMENT赋值

  • innodb_autoinc_lock_mode=0:统一使用AUTO-INC 锁
  • innodb_autoinc_lock_mode=2:统一使用轻量级锁
  • innodb_autoinc_lock_mode=1:插入记录数确定时,采用轻量级锁;不确定时使用AUTO-INC 锁

 

2.2.4 意向锁 (Intention Locks)

 

InnoDB supports multiple granularity locking which permits coexistence of row locks and table locks. For example, a statement such as LOCK TABLES … WRITE takes an exclusive lock (an X lock) on the specified table. To make locking at multiple granularity levels practical, InnoDB uses intention locks. Intention locks are table-level locks that indicate which type of lock (shared or exclusive) a transaction requires later for a row in a table. There are two types of intention locks:

  • An intention shared lock (IS) indicates that a transaction intends to set a shared lock on individual rows in a table.
  • An intention exclusive lock (IX) indicates that a transaction intends to set an exclusive lock on individual rows in a table.

假设有这样的一种场景:我们想对某张表加X锁,此时就必须先保证表中的记录都没有被加S锁和X锁。那么该如何去检测呢?可以采用循环遍历每一条记录有没有被上锁,这种方式明显效率太低了。所以InnoDB设计了另一种特殊的表级锁——意向锁。使用它是为了表在后续被加上X锁或者S锁时,能快速判断表记录之前是否有被加锁,从而避免通过遍历的方式一个个去检测行锁的存在。

意向锁也分为意向共享锁(IS)和意向排它锁(IX)

  • 意向共享锁(IS):当事务准备给表记录加S锁时,需要先对表加上IS锁
  • 意向排它锁 (IX) :当事务准备给表记录加X锁时,需要先对表加上IX锁

表级别锁的兼容性如下:

兼容性S锁IS锁X锁IX锁
S锁 兼容 兼容 不兼容 不兼容
IS锁 兼容 兼容 不兼容 兼容
X锁 不兼容 不兼容 不兼容 不兼容
IX锁 不兼容 兼容 不兼容 兼容

(表1)

其中,IS锁和IX锁、IS锁和IS锁、IX锁和IX锁之间都是兼容的。这个如何理解呢?

刚刚有提到,意向锁是为了可以快速判断表记录是否被加了锁,方便判断事务是否可以对表加锁。这就意味着,不管有事务对表记录中加了S锁,还是加了X锁,只需要加上对应的IS锁和IX锁就好了,不需要关心其他事务加的是IS锁还是IX锁。

也就是说,IS锁和IX锁只是为了后续对表加S锁或者X锁时才起作用。

  • IS锁不兼容表级X锁,兼容表级S锁。意思是表中记录加了S锁的,只允许对表整体加S锁
  • IX锁不兼容表级X锁和S锁。表中记录加了X锁的,不只允许对表整体加S锁和X锁

 

2.3 行级锁

 

如果说表级锁是对整个表进行加锁的话,那么顾名思义行级锁就是以行为单位进行加锁的机制。

  • 表级锁:优点在于加锁开销小,速度快,但锁的粒度粗,缺点是并发性能低。
  • 行级锁:相对开销较大,速度较慢,但锁的粒度细,并发性能更高,更适合OLTP的场景。

MySQL 的行级锁是在引擎层由各个引擎自己来实现的。行级锁也是 InnoDB引擎对比传统的MyISAM引擎的一大优势特性。下面重点介绍一下InnoDB中行级锁的类型。

2.3.1 Record Locks

A record lock is a lock on an index record. Record locks always lock index records, even if a table is defined with no indexes. For such cases, InnoDB creates a hidden clustered index and uses this index for record locking.

Record Lock直译过来就是记录锁。但Record Lock锁的都是索引的记录,作用于聚簇索引或者二级索引之上。即使一个表没有定义索引,InnoDB也会自动创建一个隐藏的聚簇索引并使用该索引进行记录锁定,所以Record Lock也称为索引记录锁

对于下面的例子:

SELECT c1 FROM t WHERE c1 = 10

使用show engine innodb status命令查看:

RECORD LOCKS space id 58 page no 3 n bits 72 index PRIMARY of table test.t
trx id 10078 lock_mode X locks rec but not gap
Record lock, heap no 2 PHYSICAL RECORD: n_fields 3; compact format; info bits 0
0: len 4; hex 8000000a; asc ;;
1: len 6; hex 00000000274f; asc "O;;
2: len 7; hex b60000019d0110; asc ;;

其中记录锁也分为共享记录锁和排他记录锁,同样遵循读读共享,读写互斥,写写互斥的原则。

 

2.3.2 Gap Locks

 

A gap lock is a lock on a gap between index records, or a lock on the gap before the first or after the last index record.

Gap Lock直译过来就是间隙锁。间隙锁的引入是作为记录锁的补充。我们知道MySQL在可重复读RR隔离级别下,是可以解决大部分幻读问题的。

幻读:指一个事务在前后两次查询同一个范围的时候,后一次查询看到了前一次查询没有看到的行

  • RR级别下,事务中如果是使用快照读(也称一致性读)的,如:普通的select查询,会利用MVCC的一致性视图方案来避免幻读。
  • RR级别下,事务中如果是使用当前读的,如:加锁的select语句和更新语句(更新数据都是先读后写的,此时的【读】,必须读当前的值,故称为“当前读”)。 只能用加锁的方案来避免幻读。

假设在没有间隙锁的时候,MySQL只能使用Record Lock记录锁来对数据进行加锁,但是Record Lock只作用在索引行数据上,没办法限制住范围的数据
比如下面这条语句:

select * from t where id>1 and id<5 for update
(注:表中只有id=1和id=5这两条数据)

在RR隔离级别下,如果只对id=1和id=5这两行记录加锁,就没办法限制住其他事务在(1,5)这个范围之间插入新的记录,所以引入了Gap Lock间隙锁来对索引行(1,5)之间的空隙,也加上锁。


对于行级锁来说,和行锁产生冲突的是对同一行数据加锁另外的行锁,兼容关系如下:

兼容性S锁X锁
S锁 兼容 不兼容
X锁 不兼容 不兼容

但是对于间隙锁,他们之间也有共享间隙锁和排他共享锁,但是间隙锁之间是没有冲突的,与间隙锁产生冲突的是:向间隙中间插入数据的操作。也就再一次印证了间隙锁的作用只是为了防止幻读问题。

2.3.3 Next-Key Locks

A next-key lock is a combination of a record lock on the index record and a gap lock on the gap before the index record.

Next-Key Lock 就是Record Lock+Gap Lock,锁住行记录,以及中间的空隙。
还是举例下面这条语句:

select * from t where id>1 and id<5 for update (注:表中只有id=1和id=5这两条数据)

  • Record Lock锁的范围就是id=1和id=5
  • Gap Lock锁的范围就是(1,5)
  • Next-Key Lock锁的范围就是(1,5]
    (有关记录锁和间隙锁的加锁情况比较复杂,和隔离级别,索引是二级索引还是聚簇索引直接相关,后续文章会进一步分析)

问题

间隙锁和 next-key lock 的引入,在为了解决RR隔离级别下出现幻读的问题。但同时由于锁住更大的范围,在一定程度上影响了并发性能。

解决方案

虽然RR是MySQL默认的隔离级别,但是很多线上业务系统都会选择使用RC读提交作为默认的隔离级别,同时将binlog_format设置为row。因为RC级别是允许幻读情况发生的,所以绝大部分场景下RC是不会采用间隙锁的方式(外键场景可能会使用),binlog_format设置为row则是为了防止可能出现数据和日志不一致的问题。

2.3.4 插入意向锁(Insert Intention Locks )

An insert intention lock is a type of gap lock set by INSERT operations prior to row insertion. This lock signals the intent to insert in such a way that multiple transactions inserting into the same index gap need not wait for each other if they are not inserting at the same position within the gap. Suppose that there are index records with values of 4 and 7. Separate transactions that attempt to insert values of 5 and 6, respectively, each lock the gap between 4 and 7 with insert intention locks prior to obtaining the exclusive lock on the inserted row, but do not block each other because the rows are nonconflicting.

介绍间隙锁的时候,我们知道,在某个索引区间如(1,5)加上间隙锁后,是无法插入id=3和id=4的数据,除非该间隙锁被释放。
当两个事务分别执行插入id=3和id=4的记录时,会在区间上加插入意向锁且锁状态是等待状态(is_waiting=true),等到间隙锁释放时,将插入意向锁状态is_waiting=false,唤醒两个插入的事务,且这两个事务之间是不阻塞的。

  • 插入意向锁是在INSERT插入操作时设置的一种特殊间隙锁 ,注意它并不属于意向锁而是属于间隙锁。
  • 插入意向锁之间互不排斥,当多个事务在同一区间插入记录时,只要记录本身(主键索引、唯一索引)不发生冲突,那么事务之间也不会阻塞等待。

 

三、死锁

 

A deadlock is a situation where different transactions are unable to proceed because each holds a lock that the other needs. Because both transactions are waiting for a resource to become available, neither ever release the locks it holds.
死锁是指不同事务之间每个事务都持有其他事务需要获取的锁资源,导致事务无法继续进行的情况。因为事务都在等待资源变得可用,但都不会释放它持有的锁。

也就是当不同线程并发执行出现资源依赖循环,涉及的线程都在等待别的线程释放资源时,就会导致这几个线程都进入无限等待的状态,称为死锁。

出现死锁后,一般有两种策略,第一种是:

不作处理,直到锁超时,超时后的事务会进行回滚释放锁资源,另外的事务就能继续执行。锁超时时间可以通过参数 innodb_lock_wait_timeout 来设置。

innodb_lock_wait_timeout 的默认值是 50s,这对于在线业务而言,是难以接受的,如果将超时时间改小,又可以误伤到其他正常的操作。

所以一般使用的是第二种策略:

  • 使用wait-for graph算法主动进行发起死锁检测,发现死锁后,主动回滚死锁链条中的某一个事务(一般是回滚影响行最小的事务),从而释放锁让其他事务可以继续执行。将参数 innodb_deadlock_detect 设置为 on(默认on),表示开启这个逻辑。

但是如果出现“热点行”更新的情况——很多事务都要更新同一行的数据,此时死锁检测就需要消耗大量的 CPU 资源,此时必须要限制访问相同资源的并发事务数

MySQL避免死锁的方法

1. 一次性锁定所有需要的资源
2. 按照一致的顺序进行加锁
3. 缩小锁冲突的范围

  • 避免长事务,将事务拆解。
  • 事务需要锁多个行时,尽量将最可能造成锁冲突和影响并发度的锁申请操作放在后面。
  • 在业务允许不可重复读和幻读的情况下,可使用使用RC的隔离级别,避免间隙锁锁定范围过大造成的死锁。
  • 为DML语句加上合适的索引,防止由于不走索引时为表每一行记录添加上锁。

 

四、小结

 

本文系统性介绍了MySQL&InnoDB的锁机制。按照锁的作用范围,主要分为全局锁、表锁和行锁,而共享锁和排它锁则定义了锁的互斥方式。同时介绍了死锁的发生、检测机制和如何避免死锁的方法。

  • 使用共享锁,可以提高读操作并发性能;
  • InnoDB使用行记录锁和间隙锁,为了保证RR可重复读级别下的强一致性解决,幻读问题;
  • InnoDB使用插入意向锁,可以提高插入并发性能;

在这里插入图片描述

 

参考资料

  1. MySQL官方文档
  2. 《MySQL技术内幕-InnoDB存储引擎》
  3. 《MySQL是怎样运行的-从跟上理解MySQL》
  4. 极客时间专栏《MySQL实战45讲》

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

MySQL&InnoDB锁机制全面解析

下载Word文档到电脑,方便收藏和打印~

下载Word文档

猜你喜欢

MySQL&InnoDB锁机制全面解析

本文系统性介绍了MySQL&InnoDB的锁机制。  目录一、前言二、锁的类型2.1 全局锁2.2 表级锁2.2.1 表锁2.2.2 元数据锁(Meta Data Locks)2.2.3 自增列锁(AUTO-INC Locks)2.2.4 意向锁 (In
MySQL&InnoDB锁机制全面解析
2020-07-08

AI&机器学习全面解析

欢迎各位阅读本篇,机器学习(Machine Learning,ML)是一门多领域交叉学科,涉及概率论、统计学、逼近论、凸分析、算法复杂度理论等多门学科。本篇文章讲述了AI&机器学习全面解析,编程学习网教育平台提醒各位:本篇文章纯干货~因此大家一定要认真阅读本篇文章哦!
AI&机器学习全面解析
2024-04-23

Mysql InnoDB的锁定机制实例详解

1.InnoDB的锁定机制InnoDB存储引擎支持行级锁,支持事务处理,事务是有一组SQL语句组成的逻辑处理单元,他的ACID特性如下:原子性(Atomicity): 事务具有原子不可分割的特性,要么一起执行,要么都不执行。一致性(Cons
2022-05-18

MySql主从复制机制全面解析

作为一个关系型数据库,MySQL内建地提供数据复制机制,这使得在使用时,可以基于其复制机制实现高可用架构等高级特性,从而使得MySQL无需借助额外的插件或其他工具就具备适用于生产环境。这是MySQL得到大面积实际应用的条件之一。 基于MyS
2022-05-20

解析MySQL锁的实现机制

MySQL 锁的实现原理解析引言:在并发访问数据库的环境中,为了保障数据的完整性和一致性,数据库系统需要实现锁机制。锁机制通过限制对共享资源的访问,确保不同的事务能够有序地访问和修改数据。MySQL作为一种常用的关系型数据库,也提供了多种锁
解析MySQL锁的实现机制
2023-12-21

MySQL中的乐观锁,悲观锁和MVCC全面解析

前言在数据库的实际使用过程中,我们常常会遇到不希望数据被同时写或者读的情景,例如秒杀场景下,两个请求同时读到系统还有库存1个,然后又先后把库存更新为0,这时候就会出现超卖的情况,这时候货物的实际库存和我们的记录就会对应不上了。 为了解决这种
2022-05-19

解析MySQL内部实现的锁机制

MySQL 锁的内部实现解析及代码示例引言:在多用户环境下,数据库中的数据可能同时被多个用户进行读写操作,这时就需要使用锁(Lock)机制来保证数据的一致性和并发控制。MySQL 是一个开源的关系型数据库管理系统,其内部实现了多种类型的锁来
解析MySQL内部实现的锁机制
2023-12-21

MySQL数据库锁机制原理解析

在并发访问情况下,很有可能出现不可重复读等等读现象。为了更好的应对高并发,封锁、时间戳、乐观并发控制(乐观锁)、悲观并发控制(悲观锁)都是并发控制采用的主要技术方式。 锁分类①、按操作划分:DML锁,DDL锁②、按锁的粒度划分:表级锁、行级
2022-05-21

深入解析MySQL中的各种锁机制

MySQL 各种锁详解一、引言在并发访问中,数据库需要使用锁来保护数据的一致性和完整性。MySQL 提供了多种类型的锁,包括共享锁、排他锁、意向共享锁、意向排他锁等。本文将使用具体的代码示例介绍并解析这些锁的使用方式和特点。二、共享锁(Sh
深入解析MySQL中的各种锁机制
2023-12-21

MySQL锁机制在INSERT中的深入解析

在MySQL中,当执行INSERT操作时,会涉及到锁机制来确保数据的一致性和并发性。主要涉及到的锁类型包括行级锁和表级锁。行级锁:在MySQL中,行级锁是最细粒度的锁,它只会锁定要操作的行,而不会锁定整个表。当执行INSERT操作时,My
MySQL锁机制在INSERT中的深入解析
2024-08-19

Java CGLib动态代理机制(全面解析)

一、首先说一下JDK中的动态代理:JDK中的动态代理是通过反射类Proxy以及InvocationHandler回调接口实现的但是,JDK中所要进行动态代理的类必须要实现一个接口,也就是说只能对该类所实现接口中定义的方法进行代理,这在实际编
2023-05-31

JavaScript错误处理机制全面分析讲解

下面小编就为大家带来一篇全面了解javascript中的错误处理机制。小编觉得挺不错的,现在就分享给大家,也给大家做个参考。一起跟随小编过来看看吧
2022-11-13

Java ActiveMQ:全面解析消息持久化机制

Java ActiveMQ消息持久化机制是保证消息可靠传输的关键技术,它为系统提供消息可靠传输的保障。本文将详细介绍Java ActiveMQ消息持久化的机制,包括持久化策略、持久化配置和性能优化等。
Java ActiveMQ:全面解析消息持久化机制
2024-02-08

编程热搜

目录