MySQL系列5|MySQL的锁机制

锁是计算机协调多个进程或线程并发访问某一资源的机制。为保证数据的一致性,需要对并发操作进行控制 ,因此产生了锁 。同时锁机制也为实现MySQL 的各个隔离级别提供了保证。 锁冲突也是影响数据库 并发访问性能的一个重要因素。
对于数据库中并发事务的读-读情况并不会引起什么问题。对于写-写读-写写-读这些情况可能会引起一些问题。为了满足读-读情况不受影响,又要使写-写读-写写-读情况中的操作相互阻塞,MySQL实现了两种锁

  • 读锁Lock_S,:称为共享锁 、也用表示S表示
  • 写锁Lock_X :称为排他锁 、也用X表示

对于 InnoDB 引擎来说,读锁和写锁可以加在表上,也可以加在行上。兼容性如下:


image.png

MySQL的锁从锁粒度上可以分为全局锁、表锁、行级锁。全局锁一般开发中用的比较少。本文重点介绍表锁和行锁。其中,行级锁只在InnoDB 引擎实现。

1 全局锁


全局锁就是对整个数据库实例加锁,通常用于全局的逻辑备份。MySQL 提供了一个加全局读锁的命令

Flush tables with read lock

可以让整个库都处于只读状态 。这个命令在主库上执行,更新命令都被阻塞。在从库上,binlog不能执行。

mysqldump使用参数--single-transaction,启动一个事务,确保拿到一致性视图。而由于MVCC的支持,这个过程中数据是可以正常更新的

2 表级锁


2.1 MySQL Server 的表锁

MySQL Server 层的表锁一般在执行 DDL 时使用。最常见的是元数据锁(meta data lock,MDL)。MDL不需要显式使用:

  • 当对⼀个表做增删改查操作的时候,默认加MDL读锁;
  • 当对⼀个表结构做变更操作时,加MDL写锁;
  • 事务提交后MDL锁⾃动释放。

也可以用以下命令显示的使用表锁:

lock tables t read;   // 加读锁
lock tables t2 write;  // 加写锁
unlock tables;  // 解锁

2.2 InnoDB的表锁

InnoDB的表级别锁包含五种锁模式:LOCK_IS、LOCK_IX、LOCK_X、LOCK_S以及LOCK_AUTO_INC锁。

2.2.1 表级共享锁/排他锁

表级排他锁LOCK_X

当加了LOCK_X表级锁时,所有其他的表级锁请求都需要等待。加表级LOCK_X的场景:

  • DDL操作的最后一个阶段,以确保没有别的事务持有表级锁

通常情况下,在DDL的commit 阶段是加了Server层的排他的MDL锁的。但诸如外键检查或者刚从崩溃恢复的事务正在进行某些操作,这些操作都是直接InnoDB,而不走server层。

  • 当设置会话的autocommit变量为OFF时,执行LOCK TABLE t WRITE这样的操作
  • 对某个表空间执行discard或者import操作
表级共享锁LOCK_S

加表级LOCK_S的场景:

  • 在DDL的第一个阶段,如果当前DDL不能通过ONLINE的方式执行,则对表加LOCK_S锁

  • 设置会话的autocommit为OFF,执行LOCK TABLE t READ时,会加LOCK_S锁

2.2.2 意向锁(LOCK_IS/LOCK_IX)

InnoDb 支持的意向锁为表级别的。
意向锁可以理解为一种“暗示”未来需要什么样行级锁,LOCK_IS表示未来可能需要在这个表的某些记录上加共享锁S,LOCK_IX表示未来可能需要在这个表的某些记录上加排他锁X。意向锁不会与行级的S / X互斥

意向共享锁 (IS Lock) 和意向排他锁 (IX Lock)
  • 意向共享锁,事务想获得一张表中某几行的共享锁;要获取某些行的 S 锁,必须先获得表的 IS 锁。
  • 意向排他锁,事务想获得一张表中某几行的排他锁。要获取某些行的 X 锁,必须先获得表的 IX 锁。

意向锁是有数据引擎自己维护的,用户无法手动操作意向锁,在为行加共享锁S / 排他锁X之前,InooDB 会先获取该数据行所在在数据表的对应意向锁。

意向锁要解决的问题

以表user为例,考虑下列场景:

事务 A 获取了user表某一行的排他锁,并未提交:
事务 B 想要获取 users 表的表锁:

此时事务 B 检测事务 A 持有 users 表的意向排他锁,就可以得知事务 A 必然持有该表中某些数据行的排他锁,那么事务 B 对 users 表的加锁请求就会被排斥,而无需去检测表中的每一行数据是否存在排他锁。

意向锁兼容性
  • 意向锁之间互相兼容


    image.png
  • 意向锁与表级别的排他锁/共享锁的兼容性


    image.png

    注意这里的排他锁和共享锁指的都是表锁,意向锁不会与行级的共享锁或排他锁互斥。

2.3 自增锁LOCK_AUTO_INC

自增锁是当向使用含有AUTO_INCREMENT列的表中插入数据时需要获取的一种特殊的表级锁,在执行插入语句时就在表级别加一个AUTO-INC锁,然后为每条待插入记录的AUTO_INCREMENT修饰的列分配递增的值,在该语句执行结束后,再把AUTO-INC锁释放掉。

3 行级锁


MySQL中只有InnoDB支持行级锁。行锁按照不同的实现方式,可以分为记录锁、间隙锁、临键锁。

3.1 间隙锁、临键锁、记录锁

3.1.1记录锁(LOCK_REC_NOT_GAP)

也叫行锁, 仅仅锁住索引记录的一行,在单条索引记录上加锁。
行级锁并不是直接锁记录,而是锁索引。即使该表上没有任何索引,也会创建一个隐藏的聚集主键索引。

  • 如果一条sql语句操作了主键索引,MySQL就会锁定这条主键索引;
  • 如果一条语句操作了非主键索引,MySQL会先锁定该非主键索引,再锁定相关的主键索引。

只有唯一索引且是唯一等值查询时,才有记录锁,锁定单条索引记录。记录锁是有S锁和X锁之分的,称之为 S型记录锁 和 X型记录锁 :

  • 当获取了一条记录的S型记录锁后,其他事务也可记录的S型记录锁,但不可以获取X型记录锁;
  • 当一个事务获取了一条记录的X型记录锁后,其他事务既不可以S型记录锁或X型记录锁。
3.1.2 间隙锁 (LOCK_GAP)

间隙锁只锁住一段范围,不锁记录本身,通常表示两个索引记录之间,或者索引上的第一条记录之前,或者最后一条记录之后的锁。是一种区间锁,
间隙锁存在于非唯一索引(包括非唯一索引的等值查询),或者范围查询中(包括唯一索引的范围查询),锁定开区间范围

注意

  • 间隙锁在本质上是不区分共享或互斥的,而且间隙锁是不互斥
  • 间隙锁只存在RR隔离级别下,Gap lock的作用时阻止多个事务将数据插入同一范围,导致phantom Problem问题。
    在RU和RC两种隔离级别下,即使你使用select ... in share mode/ for update,也无法防止幻读。因为这两种隔离级别下只会有行锁,而不会有间隙锁。
3.1.3 临键锁(LOCK_ORDINARY ,Next-Key Lock)

临键锁,是记录锁与间隙锁的组合,它的封锁范围,包含记录本身及记录之前的GAP。是一个左开右闭区间。
在UPDATE、DELETE操作时,MySQL不仅锁定WHERE条件扫描过的所有索引记录,而且会锁定相邻的键值,即所谓的next-key locking。
间隙锁存在于非唯一索引(包括非唯一索引的等值查询),或者范围查询中(包括唯一索引的范围查询),锁定一段左开右闭的索引区间

3.2 行级共享锁/排他锁

行锁也有读锁和写锁两种模式,即共享锁(LOCK_S)排他锁(LOCK_X)。通常共享锁排他锁不特殊说明(表级)默认指行级。

3.2.1 共享锁LOCK_S

共享锁的作用通常用于在事务中读取一条行记录后,不希望它被别的事务锁修改。如下几种情况会请求LOCK_S锁:
情况一:SELECT … IN SHARE MODE
基于不同的隔离级别,行为有所不同:

  • RC隔离级别:LOCK_RECORD | LOCK_S
  • RR隔离级别:如果查询条件为唯一索引且是唯一等值查询时, 加的是
    LOCK_RECORD | LOCK_S;对于非唯一条件查询,或者查询会扫描到多条记录时, 加的是 Next-Key Lock | LOCK_S锁;

情况二:duplicate类型的INSERT
通常普通的INSERT操作是不加锁的,但如果在插入或更新记录时,检查到 duplicate key(或者有一个被标记删除的duplicate key),会加LOCK_S锁(对于类似REPLACE INTO或者INSERT … ON DUPLICATE这样的SQL加的是X锁)。
针对不同的索引类型也有所不同

  • 对于聚集索引
    RC隔离级别:LOCK_RECORD | LOCK_S;
    RR隔离级别:Next-Key Lock | LOCK_S
  • 对于二级唯一索引
    当前版本总是加 Next-Key Lock 类型锁

情况三:普通查询在隔离级别为 SERIALIZABLE 会给记录加 LOCK_S 锁

3.2.2 排他锁 LOCK_X

排他锁的目的主要是避免对同一条记录的并发修改。通常对于UPDATE或者DELETE操作,或者类似SELECT … FOR UPDATE操作,都会对记录加LOCK_X。

情况一:通过二级索引查询

  • RC隔离级别:
    锁住二级索引记录,为LOCK_REC_NOT_GAP | LOCK_X锁;
    锁住对应的聚集索引记录,也是LOCK_RECORD | LOCK_X锁

  • RR隔离级别下:
    锁住二级索引记录,为Next-Key Lock| LOCK_X锁;
    锁住聚集索引记录,为LOCK_RECORD | LOCK_X锁

情况二:通过聚集索引检索,更新二级索引数据

  • 对聚集索引记录加 LOCK_REC_NOT_GAP | LOCK_X锁;
  • 修改记录总是先聚集索引,再二级索引的顺序,即使不对二级索引加锁也没有关系。但如果已经有别的线程已经持有了二级索引上的记录锁,则需要等待

3.3 插入意向锁 (Insert Intention Lock)

插入意向锁是在插入一条记录行前,由 INSERT 操作产生的一种间隙锁。该锁用以表示插入意向,当多个事务在同一区间(gap)插入位置不同的多条数据时,事务之间不需要互相等待。

插入意向锁控制和解决并发插入

MySql InnoDB 在 Repeatable-Read 的事务隔离级别下,使用插入意向锁来控制和解决并发插入。

  • 插入意向锁是一种特殊的间隙锁。
  • 插入意向锁在锁定区间相同但记录行本身不冲突的情况下互不排斥。

假设存在两条值分别为 4 和 7 的记录,两个不同的事务分别试图插入值为 5 和 6 的两条记录,每个事务在获取插入行上独占的(排他)锁前,都会获取(4,7)之间的间隙锁,但是因为数据行之间并不冲突,所以两个事务之间并不会产生冲突(阻塞等待)

为什么不用间隙锁

因为事务B插入的记录6位于(4,7)区间内,而该区间内又存在一把间隙锁(插入记录5的事务A),而间隙锁的作用是阻止多个事务将数据插入同一范围 。所以事务B 只能等待 事务 A 结束,才能执行插入操作。这样做事务之间将会频发陷入阻塞等待,插入的并发性非常之差。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 205,386评论 6 479
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,939评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,851评论 0 341
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,953评论 1 278
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,971评论 5 369
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,784评论 1 283
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,126评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,765评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,148评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,744评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,858评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,479评论 4 322
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,080评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,053评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,278评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,245评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,590评论 2 343

推荐阅读更多精彩内容