MySQL 篇-深入了解 MySQL 锁的机制(表锁、意向锁、行锁、间隙锁、临键锁等)

🔥博客主页: 【小扳_-优快云博客】
❤感谢大家点赞👍收藏⭐评论✍

文章目录

        1.0 锁的概述

        2.0 全局锁概述

        2.1 全局锁的特点

        3.0 表级锁概述

        3.1 表级锁 - 表锁

        3.2 表级锁 - 元数据锁

        3.3 表级锁 - 意向锁

        4.0 行级锁概述

        4.1 行级锁 - 行锁(Record Lock)

        4.2 行级锁 - 间隙锁(Gap Lock)、临键锁(Next-Key Lock)


        1.0 锁的概述

        锁是计算机协调多个进程或线程并发访问某一个资源的机制。在数据库中,除传统的计算资源(CPU、RAM、I/O)的争用以外,数据也是一种供许多用户共享的资源。如何保证数据并发访问的一致性、有效性是所有数据库必须解决的一个问题,锁冲突也是影响数据库并发访问性能的一个重要因素。从这个角度来说,锁对数据库而言显得尤其重要,也更加复杂。

MySQL 中的锁,按照锁的粒度分,分为以下三类:

        1)全局锁:锁定数据库中的所有表。

        2)表级锁:每次操作锁住整张表。

        3)行级锁:每次操作锁住对应的行数据。

        2.0 全局锁概述

        全局锁就是对整个数据库实例加锁,加锁后整个实例就处于只读状态,后续的 DML 的写语句,DDL 语句,已经更新操作的事务提交语句都将被阻塞。

        其典型的使用场景是做全库的逻辑备份,对所有的表进行锁定,从而获取一致性视图,保证数据的完整性。

        做表的备份步骤:

        1)对全表进行加锁:

flush tables with read lock;

        2)选择具体的库,将该库中的所有表进行备份

mysqldump -uroot -p你的数据库密码 选择的库名 > 备份的位置
-- 例如:
mysqldump -uroot -p123456 db06 > D:/db06.sql

        需要注意的是,该命令执行不能在 MySQL 中执行,因为该语句不属于 SQL 语句,在命令框中执行该语句即可。 

 运行结果:

        这就成功备份了目的库中的所有表数据和表结构。

         3)释放锁

unlock tables;

 流程图:

        2.1 全局锁的特点

        数据库中加全局锁,是一个比较重的操作,存在以下问题:

        1)如果在主库上备份,那么在备份期间都不能执行更新,业务基本上就得停摆。

        2)如果在从库上备份,那么在备份期间从库不能执行主库同步过来的二进制日志,会导致主从延迟。

        如果备份不想加上全局锁,那么在 InnoDB 引擎中,可以在备份时加上参数 --single-transaction 参数来完成不加锁的一致性数据备份。

mysqldump --single-transaction  -uroot -p123456 db06 > D:/db06.sql

        3.0 表级锁概述

        表级锁,每次操作锁住整张表。锁定粒度大,发生锁冲突的概率较高,并发度最低。应用在 MyISAM、InnoDB、BDB 等存储引擎中。

        对于表级锁,主要分为以下三类:

        1)表锁

        2)元数据锁(meta data lock)

        3)意向锁

        3.1 表级锁 - 表锁

        对于表锁,分为两类:表共享读锁、表独占写锁。

        读锁的简单解释:当线程 A 对某一张表加上了读锁,其他线程对该表只能进行读操作,不能进行写操作。

        写锁的简单解释:当线程 A 对某一张表加上了写锁,其他线程对该表既不能进行读操作,也不能进行写操作,只有线程 A 才能进行读和写操作。

相关的语法:

-- 加读锁/写锁
lock table 表名 read/write;
-- 释放锁
unlock tables;

        当客户端断开连接也会将锁释放。

举个例子:

        以上就是加了读锁的过程,而对于写锁,只能在当前线程中执行读和写操作,其他线程一律处于阻塞状态,直到当前线程释放锁。

读锁与写锁的区别:

        3.2 表级锁 - 元数据锁

        元数据所简称 MDL,MDL 加锁过程是系统自动控制,无需显式使用,在访问一张表的时候会自动加上。MDL 锁主要作用是维护无数据的数据一致性,在表上有活动事务的时候,不可用对无数据进行写入操作。为了避免 MDL 与 DDL 冲突,保证读写正确性。

        对于一张表进行数据的增删改的时候,会自动加上 shared_write 锁,而进行查询的时候,会自动加上 share_read 锁,对于 shared_write 与 shared_read 都是兼容的。

        对于一张表进行表结构的修改时,比如: alter table ......,则会加上 exclusive 锁,而 exclusive 锁对于 shared_write 或者 shared_read 是互斥的。

查看元数据锁:

select OBJECT_TYPE, OBJECT_SCHEMA, OBJECT_NAME, LOCK_TYPE, LOCK_DURATION from performance_schema.metadata_locks;

执行结果:

举个例子:

        此时,又一个线程来想要对该表进行表结构的修改:

        由于 share_read 与 exclusive 是互斥的,所以只等处于阻塞状态,直到 share_read 进行锁释放,当提交完毕之后,锁就会自动释放。

        事务一旦提交完毕,则锁就被释放。 

        观察 book 表的结构,成功新增 book_structure 列。 

        3.3 表级锁 - 意向锁

        为了避免 DML 在执行时,加的行锁与表锁的冲突,在 InnoDB 中引入了意向锁,使得表锁不用检查每行数据是否加锁,使用意向锁来减少表锁的检查。

意向锁分类:

        1)意向共享锁(IS):由语句 select ... lock in share mode 添加。与表锁共享锁 read 兼容,与表锁排他锁 write 互斥。

        2)意向排他锁(IX):由 insert、update、delete、select ... for update 添加。与表锁共享锁 read 及排他锁 write 都是互斥的。意向锁之间不会互斥。

可以通过以下 SQL,查看意向锁及行锁的加锁情况:

select OBJECT_SCHEMA, OBJECT_NAME, INDEX_NAME,LOCK_TYPE,LOCK_MODE,LOCK_DATA  from performance_schema.data_locks;

举个例子:

        1)意向共享锁 IS:

        直到 IS 被释放之后,才能添加上写锁。

         2)意向排他锁 IX:

        直到事务提交,线程 B 才会从阻塞状态转为运行状态:

        4.0 行级锁概述

        行级锁,每次操作锁住对应的行数据。锁定粒度最小,发生锁冲突的概率最低,并发度最高。应用在 InnoDB 存储引擎中。

        InnoDB 的数据是基于索引组织的,行锁是通过对索引上的索引项加锁来实现的,而不是记录加的锁。

        对于行级锁,主要为以下三类:行锁、间隙锁、临键锁。

        4.1 行级锁 - 行锁(Record Lock)

        锁定单个行记录的锁,防止其他事务对此进行 update 和 delete 。在 RC、RR 隔离级别下都支持。

InnoDB 实现了以下两种类型的行锁:

        1)共享锁(S):允许一个事务去读一行,阻止其他事务获取相同数据集的排他锁。

        2)排他锁(X):允许获取排他锁的事务更行数据,阻止其他事务获取相同数据集的共享锁和排他锁。

        简单来说,S 与 S 是兼容的,而 S 与 X 或者 X 与 X 是互斥的。

        默认情况下,InnoDB 在 REPEATABLE READ 事务隔离级别运行,InnoDB 使用 next-key 锁进行搜索和索引扫描,以防止幻读。

        1)针对唯一索引进行检索时,对已存在的记录进行等值匹配时,将会自动优化为行锁。

举个例子:

        book_id 是唯一索引,book_id = 1 是存在的且是通过等值匹配,因此, 自动添加为行锁。

查询结果:

        2)InnoDB 的行锁是针对索引加的锁,不通过索引条件检索数据,那么 InnoDB 将对表中的所有记录加锁,此时就会升级为表锁。

        升级为表锁之后,则整个表就被锁住了,其他操作只能处于阻塞状态。

        4.2 行级锁 - 间隙锁(Gap Lock)、临键锁(Next-Key Lock)

        间隙锁,锁定索引记录间隙,不含该记录,确保索引记录间隙不变,防止其他事务在这个间隙进行 insert,产生幻读。在 RR 隔离级别下都支持。

        临键锁,行锁和间隙锁的组合,同时锁住数据,并锁住数据前面的间隙 Gap,在 RR 隔离级别下支持。

        默认情况下,InnoDB 在 REPEATABLE READ 事务隔离级别运行,InnoDB 使用 next-key 进行搜索和索引扫描,以防止幻读。

        1)索引上的等值查询(唯一索引),给不存在的记录加锁时,优化为间隙锁。

举个例子:

        原始的 book 表数据,不存在 book_id 为 9 的数据:

         现在对不存在的 book_id 为 9 的数据进行唯一索引且等值匹配:

        接着查看当前锁资源:

        此时锁的类型为间隙锁,将 id 为 8 - 10 之间的空隙锁起来了,而 id 为 8 和 10 的行数据不会被锁起来,锁的只是间隙。

        2)索引上的等值查询(普通索引),向右遍历最后一个值不满足查询需求时,next-key lock 退化为间隙锁。

举个例子:

        表数据:

        根据 age 进行等值查询,且 age 只是普通索引。

        查看当前锁资源:

        可以发现,在 3 加上了临键锁,将 3 的行数据锁上,还把 3 之前的全部间隙锁上,再有 3 的行锁,将 3 的行数据锁上,再接着将 3 - 7 之间的全部间隙锁上。注意的是,7 的行数据没有被锁上。

        3)索引上的范围查询(唯一索引),会访问到不满足条件的第一个值为止。

举个例子:

        表数据:

        根据 id 进行范围查询:

        查看当前锁资源:

        可以发现,对 19 加上了行锁,给 25 加上了临建锁,还给正无穷( +∞ )到 25 加上了临键锁。

        间隙锁的唯一目的是防止其他事务插入间隙。间隙锁可以共存,一个事务采用的间隙锁不会阻塞另一个事务在同一个间隙上采用间隙锁。

评论 81
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小扳

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值