关于mysql:MySQL-中的锁机制

32次阅读

共计 4240 个字符,预计需要花费 11 分钟才能阅读完成。

技术是为了解决问题而生的,锁被用来实现隔离性,保障并发事务的正确性。上面将具体介绍一下锁机制。

两段锁 & 一次封闭

两段锁

数据库遵循的是两段锁协定,将事务分成两个阶段,加锁阶段和解锁阶段(所以叫两段锁)

加锁阶段:在加锁阶段只能进行加锁操作。

如果事务要读取对象,必须先取得共享锁。能够有多个事务同时取得一个对象的共享锁

如果事务要批改对象,必须先取得独占锁。只能有一个事务取得对象的独占锁。如果某个事务曾经取得了对象的独占锁,则其余尝试获取锁(包含共享锁、独占锁)的事务必须期待,直到加锁胜利能力继续执行

解锁阶段:在解锁阶段只能进行解锁操作。

事务要读取对象,必须先取得共享锁,这样避免幻读。事务要批改对象,必须先取得独占锁,这样避免脏写。

两段锁能够这样来实现:事务开始后就处于加锁阶段,始终到执行 rollback 或 commit 之前都是加锁阶段。rollback 和 commit 使事务进入解锁阶段,即在 rollback 或 commit 时开释持有的锁。

一次封闭

一次封闭法恪守两段锁协定。

一次封闭要求每个事务必须一次将所有要应用的数据全副加锁,否则就不能继续执行。

一次封闭存在的问题:

封闭工夫被缩短,并发度被升高:一次就将当前要用到的全副数据加锁,势必缩短了封闭的工夫,从而升高了零碎的并发度。

不适宜用在数据库:一次封闭不适宜用在数据库中,因为在事务开始阶段,数据库并不知道会用到哪些数据。

一次封闭的益处:不会呈现死锁。

为什么要应用两段锁呢?用完间接开释锁不行吗?

不行,用完间接开释会使事务的隔离性受到影响。具体介绍能够看上面的文章。Mysql 锁:灵魂七拷问 (youzan.com)

两段锁 的优劣局限

两段锁的长处 / 作用:

解决事务并发问题:避免脏写、脏读 ……

实现可串行化隔离:将两段锁与谓词锁联合应用,能够避免所有模式的写歪斜以及其余竞争条件,实现可串行化隔离

性能和理论串行相比:相比于理论串行来说,应用两段锁时,多个事务能够并发读取同一个对象

性能和一次封闭来比:相比于一次封闭,两段锁的锁定工夫更短,事务并发性比一次封闭要好

两段锁的毛病:

性能:应用两段锁,事务吞吐量和查问响应工夫相比于其余弱隔离级别降落十分多。局部起因在于锁的获取和开释自身的开销,但更重要的是其升高了事务的并发性。

拜访提早具备十分大的不确定性:如果一个事务须要期待另一个事务开释锁,另一个事务开释锁的机会是不确定的,因而期待它开释锁的耗时是不确定的。

死锁更加频繁:因为两段锁的加锁模式,死锁可能变得更为频繁。因此导致另一个性能问题,即如果事务因为死锁而被强行停止,应用层就必须从头重试,如果死锁过于频繁,则性能和效率必然大打折扣。

数据库系统会自动检测死锁状况,并强行停止其中的一个事务以突破僵局

因为应用了这么多的锁机制,所以很容易呈现死锁景象,例如事务 A 可能在期待事务 B 开释它持有的锁,而事务 B 在期待事务 A 开释它持有的锁。数据库系统会自动检测事务之间的死锁状况,并强行停止其中的一个事务以突破僵局,这样另一个能够持续向前执行。而被停止的事务须要由应用层来重试。

MySQL 提供的锁

依据加锁的范畴,MySQL 外面的锁大抵能够分成全局锁、表级锁和行级锁三类。

全局锁

全局锁就是对整个数据库实例加锁。

给数据库实例加全局锁的命令:flush tables with read lock;(FTWRL)

开释锁的命令:unlock tables;(表级锁、行级锁开释也是这个命令)

加上全局锁之后,整个数据库处于只读状态,其余线程的以下语句会被阻塞:

数据更新语句(数据的增删改 insert、delete、update)

数据定义语句(DDL、包含建表、批改表构造等)

更新类事务的提交语句(更新类事务就是应用了相似 select * from t1 for update; 带 for update 的查问的事务)

全局锁的典型应用场景是,做全库逻辑备份。也就是把整库每个表存成 .sql 类型的文件。

全局锁的作用相当于是进行更新操作,拿到一个一致性视图。

MySQL 的在可反复读隔离级别下开启一个事务也能够拿到一个一致性视图,并且后者能够做到不影响更新操作。

官网自带了全量逻辑备份工具 mysqldump。

当 mysqldump 应用参数 –single-transaction 的时候,就会应用基于 MVCC 机制的一致性视图。

当 mysqldump 应用参数 –master-data 的时候,就会应用基于全局读锁的一致性视图。

表级锁

表级锁就是对表加锁。

MySQL 外面表级别的锁有三种:

表锁;

元数据锁(meta data lock,MDL);

意向锁。

表锁

表锁就是对整个数据表加锁。

给数据表加表锁的命令:lock table 表名 read /write;

开释表锁的命令和开释全局锁的命令一样,都是:unlock tables;。如果不手动开释表锁,在客户端断开的时候会主动开释表锁。

须要留神的是,lock tables 语法除了会限度别的线程的读写外,也限定了本线程接下来的操作对象。

举个例子,如果在线程 A 中执行 lock tables t1 read, t2 write; 这个语句,则其余线程写 t1、读写 t2 的语句都会被阻塞。同时,线程 A 在执行 unlock tables 之前,也只能执行读 t1、读写 t2 的操作。连写 t1 都不容许,天然也不能拜访其余表。

意向锁

表锁分为:共享锁、独占锁。

如果咱们想对整个数据表加共享锁,首先要确保表中没有记录被加独占锁

如果咱们想对整个数据表加独占锁,首先要确保表中没有记录被加共享锁 / 独占锁

那么咱们该如何来判断表中是否有记录被加独占锁 / 独占锁呢?咱们能够通过遍历所有记录的形式来查看表中有没有被加锁的记录,而遍历的形式太慢了。

意向锁的提出就是为了加表级别的共享锁 和 独占锁时,疾速判断表中的记录是否被上锁,以防止用遍历的形式来查看表中有没有被加锁的记录,提供判断速度。

意向锁分为:动向共享锁、动向独占锁:

当事务筹备在某条记录上加 共享锁 时,须要先在表级别加一个 动向共享锁;

当事务筹备在某条记录上加 独占锁 时,须要先在表级别加一个 动向独占锁。

这样,如果表级别存在 动向共享锁,就意味着表中有被加 共享锁 的记录;如果表级别存在 动向独占锁,就意味着表中有被加 独占锁 的记录。通过意向锁咱们就能够疾速判断表中是否有记录被加锁。

元数据锁

元数据锁(meta data lock,MDL)是 MySQL 5.5 版本引入的。

MDL 不须要显式应用,在拜访一个表的时候会被主动加 MDL 锁。

MDL 锁分为:MDL 读锁、MDL 写锁:

DML 操作(数据的增删改查:insert、delete、update、select)加 MDL 读锁

DDL 操作(对表构造做变更操作)加 MDL 写锁。

MDL 锁的加锁、开释锁的规定:

MDL 读锁与 MDL 读锁互不烦扰。

MDL 写锁与 MDL 写锁、MDL 写锁与 MDL 读锁互相阻塞。用来保障变更表构造操作的安全性。

MDL 锁应用两段锁:事务取得锁之后,始终持有锁直到事务完结(包含提交或停止)。

MDL 锁作用是:避免 DDL 操作和 DML 操作并发,保障变更表构造操作的安全性。

须要留神的是,如果申请加 MDL 锁失败,那么再此之后的加锁申请都必须期待(偏心锁机制,遵循先来先执行准则,先来的没有加锁胜利,起初的不能加锁)。因而执行 DDL 操作时要分外留神,如果操作执行工夫过长,前面的 DML 操作都将被阻塞较长时间。

行级锁

行级锁就是对记录加锁。

行级锁又分为各种类型,不同类型的行级锁的作用也不同,行级锁分为:

Record Lock:行锁,单个行记录的锁

Gap Lock:间隙锁,作用于记录与记录之间的空隙,作用仅仅是为了避免满足搜寻条件的记录插入空隙(避免插入幻影记录)

Next-Key Lock:索引区间锁,实质是一个行锁 和 一个 Gap Lock 的结合体

只在可反复读或以上隔离级别下的特定操作才会加间隙锁。在 加读写锁的 select、update 和 delete 时,除了基于惟一索引(主键索引也属于惟一索引)的查问之外,基于其余索引查问时都会加间隙锁。

可能加 Gap Lock 的要求:

必须是可反复读或以上隔离级别

如果是 select,则必须以给读到的记录加读写锁的形式

可反复读隔离级别下的 select … for update、select … lock in share mode

可串行化隔离级别下的 select …(加共享锁)以及下面两种手动加共享锁,排他锁的形式

必须是可能走索引的查问,如果是全表扫描的查问那么没有方法加 Gap Lock。

加锁规定:蕴含了两个“准则”、两个“优化”和一个“bug”。

准则 1:加锁的根本单位是 next-key lock。next-key lock 是前开后闭区间。

准则 2:查找过程中拜访到的对象才会加锁。

优化 1:索引上的等值查问,给惟一索引加锁的时候,next-key lock 进化为行锁。

优化 2:索引上的等值查问,向右遍历时且最初一个值不满足等值条件的时候,next-key lock 进化为间隙锁。

一个 bug:惟一索引上的范畴查问会拜访到不满足条件的第一个值为止。

共享锁 & 独占锁

表锁、元数据锁、行锁又都分为共享锁和独占锁。

共享锁 – 共享锁兼容:如果事务要读取对象,必须先以共享模式取得锁。能够有多个事务同时取得一个对象的共享锁

共享锁 – 独占锁、独占锁 – 独占锁互斥:如果事务要批改对象,必须先以独占模式获取锁。只能有一个事务取得对象的独占锁。如果某个事务曾经取得了对象的独占锁,则其余尝试获取锁(包含共享锁、独占锁)的事务必须期待

正文完
 0