MySQL中的锁怎么理解

发布时间:2021-12-03 17:19:12 作者:iii
来源:亿速云 阅读:155

本篇内容主要讲解“MySQL中的锁怎么理解”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“MySQL中的锁怎么理解”吧!

01. 怎么认识"锁"

02. "锁"的分类

03. 加"锁"过程

04. 给谁加"锁"

05. 加"锁"目的

06. 加"锁"对象

07. "锁"和事务

      a.  读未提交(Read Uncommitted,RU),即一个事务可以读到其他事务已操作但未提交的数据,当这个操作回滚时,即发生脏读

      b.  读已提交(Read Committed,RC),即一个事务仅能读到其他事务已提交的数据,确保这个数据是实实在在真实的数据,避免了脏读,但可能导致本事务窗口内前后查询结果不一致,即不可重复读

      c.  可重复读(Repeatable Read,RR),即可重复读,基于MVCC机制,在当前事务中的首次查询时,记录一个快照版本,同一事务期间的后续查询均采用当前快照版本的结果,所以即使是其他事务已提交的数据,但若其快照版本在本事务首次快照版本之后,也不会读出来。注意,这里当前事务采集的快照"版本号"取决于首次查询的时机,而不是开始事务的时机。

      d.  串行化(Serializable, SE),严格限制并发,多个事务间在存在数据竞争时串行执行,数据稳定性和一致性最强,但并发能力受到极大限制。注意,这里是指存在数据冲突时事务间串行,否则仍可并发

1## 开启事务2种方法  2-- 一种是显式开启事务  3START TRANSACTION / BEGIN  4-- 另一种是关闭自动提交  5SET autocommit = 0  6  7## 结束事务  8COMMIT / ROLLBACK
1select ……;  2等价于  3START TRANSACTION;  4selece ……;  5COMMIT;

08."读象"

read phenomena,官方文档给出的英文写法,未找到相关权威翻译名词。特指MySQL读取过程中存在的副作用,例如脏读、幻读等

需要指出:MySQL依靠MVCC的快照机制,某种程度上RR隔离级别已经避免了幻读,但仍可触发,官方文档也给予相应的说明。具体请阅读后面的实战案例。

09. 快照读和当前读

实战案例篇

以下所有案例均依托Navicat Primium12工具。初始建表语句:

1create table test(id int, name varchar(20), primary key(id));  2insert into test values(1, 'A');  3insert into test values(3, 'C');

10. 3种"读象"

脏读、不可重复读和幻读应该是困扰很多人的一个常见概念问题,尤其是后两者的区别,这里通过几个案例进行阐释说明。

首先来看官方文档给出的定义:

An operation that retrieves unreliable data, data that was updated by another transaction but not yet committed. It is only possible with the isolation level known as read uncommitted.

大意:某个操作中处理了由其他事务更新但尚未提交的数据,这个数据是不可靠的数据,仅发生于RU隔离级别。

案例:

MySQL中的锁怎么理解

RU存在脏读:事务A读到了事务B更改但未提交的数据

官方文档给出的定义:

The situation when a query retrieves data, and a later query within the same transaction retrieves what should be the same data, but the queries return different results (changed by another transaction committing in the meantime).

大意:在一项事务查询数据期间,由于其他事务同时进行了提交,造成其前后两次查询到的数据结果不一致。

案例:

MySQL中的锁怎么理解

RC避免了脏读,但存在不可重复读

A row that appears in the result set of a query, but not in the result set of an earlier query. For example, if a query is run twice within a transaction, and in the meantime, another transaction commits after inserting a new row or updating a row so that it matches the WHERE clause of the query.

大意:之前查询的结果中不存在、但之后查询得到的记录称作是幻读。例如,一个查询执行两次,期间另一个事务进行了插入或更新记录并提交,导致前一个事务两次查询结果不一致。

个人观点,幻读本身当然属于不可重复读的一种,毕竟两次读取结果"不一致"。但幻读侧重的是之前没有、之后虚幻出来了新行这种特定操作。

案例:

①,RR级别可避免RC级别中的不可重复读问题:

MySQL中的锁怎么理解

RR不存在不可重复读数据

②,特殊情况下仍可触发幻读

MySQL中的锁怎么理解

RR级别下,特殊操作仍可触发幻读(更新快照)

实际上,MVCC机制只是为保证读取结果采取快照的方式,所以能保证可重复读,但对于执行insert、update和delete操作时,仍然会实际检测当前数据库中最新的记录状态:当其他事务提交的最新数据与本事务中的增删改操作符合条件时,仍然会有影响。

这点不难理解,毕竟要保证数据库的状态一致性,但值得诧异的是经过update之后,居然会更新事务中的快照版本。例如图中所示案例,初次查询有2条记录,update时实际更新的是3条,但再次查询时结果也更新成了3条。而且,更重要的是,这种现象并不具有普遍性:仅当事务执行update操作时才会更新快照版本,而对于delete和insert操作则是只检测状态不更新快照版本。

MySQL中的锁怎么理解

事务的insert操作不会更新快照版本

更一般的,进一步测试了事务B执行的其他增删改操作对事务A是否更新快照版本的影响,两两组合,得到如下试验结论:

MySQL中的锁怎么理解

如上幻读仅发生在其他事务插入新记录且提交后,本事务更新数据后的再次查询中

当然,官方文档对此给出了注解:

MySQL中的锁怎么理解

大意是说:快照读(snapshot)仅适用于查询语句,对DML(数据操纵语言,即增删改操作)不适用。其他事务执行删除或更新操作并提交,当前事务虽然"看不到"这些更改,但在执行自己执行更新或删除操作后对其可见。虽然此注解足以解释上述案例结论,但笔者实际上仍然存在前述表中的疑问。

最后需要指出的是,MVCC机制是基于快照版本的并发控制,与之对应的是LBCC,当采用LBCC读取数据时,则总能读到最新的数据。当然,这与RR隔离级别和MVCC机制并不矛盾。

MySQL中的锁怎么理解

加锁读总是读取最新结果,但不影响快照版本

11. 快照版本

MVCC是基于多版本的并发控制,查询结果以快照版本为准。但不同隔离级别的快照版本采集原则不一致。在RR隔离级别中,通过MVCC机制实现了在同一事务中的可重复读取问题,而且该快照是在首次查询时采集的版本号信息,而与开启事务时机无关。

MySQL中的锁怎么理解

RR级别中首次查询建立快照版本

而且,RR级别中一旦建立了快照版本,则在该事务的后续查询中均采用该快照版本作为结果(当然,通过前面的案例发现也有例外);与之对应的是,RC级别中,每次查询都采集最新的快照版本作为结果,所以自然也就存在不可重复读的问题。

12. 加锁类型

首先简单介绍记录锁、间隙锁和临键锁:

记录锁根据索引锁定相应记录,即使相应的表中不建立任何索引时。实际上所有InnoDB表都存在索引,当用户建表时未显式设置索引时,引擎会自动建立隐藏索引,这也是InnoDB底层基于聚簇索引存取整条记录的特性使然。

MySQL中的锁怎么理解

记录锁仅对索引满足查询条件的记录加锁

如果说记录锁是对命中的记录进行加锁,那么间隙锁是则是对查询区间范围内但是不存在的记录进行预订加锁,例如下图中假设表中不存在id=2、3的记录,但因为满足查询范围,所以会对其加间隙锁。

MySQL中的锁怎么理解

间隙锁对满足查询条件的记录间隙加锁

显然,间隙锁是以牺牲一定并发性能为代价换取高一致性。实际上,这也是所有锁在做的一件事,即在一致性和并发能力之间获得某种均衡。

需要指出的是:

  1. 鸿蒙官方战略合作共建——HarmonyOS技术社区

  2.  间隙锁仅在范围查询时存在,对于等值查询则不适用,例如上例中查询条件改为where id=1 or id=4则不会对潜在的id=2和3加间隙锁

  3.  当查询条件是等值查询,但查询条件是联合索引(在多列创建的索引)时,也会对满足要求的潜在记录加间隙锁

  4.  间隙锁仅在特定隔离级别存在,RR级别中默认有间隙锁,而RC级别则不存在

在记录锁和间隙锁的基础上,临键锁=记录锁+间隙锁。

MySQL中的锁怎么理解

临键锁=记录锁+间隙锁

RC隔离级别中只有记录锁,而没有间隙锁和临键锁;RR级别中如果是等值查询则是记录锁,范围查询则是临键锁(即记录锁+间隙锁),在5.6以前版本中可以通过全局参数设置是否开启,但在8.0版本已移除该变量。

MySQL中的锁怎么理解

MySQL中的锁怎么理解

13. 索引类型对加锁影响

在明确加锁类型后,还需考虑不同索引对加锁的影响。首先指出,在InnoDB引擎下即使创建表时不显式指定索引,引擎也会自动生成隐藏索引用于聚簇存储记录数据。基于此,索引对加锁的影响有如下几种情况(引自官方文档):

不同类型下的加锁分析详见文末参考资料2中文档,讲解充分,受到广泛转发引用,这里个人就不班门弄斧了。

14. 锁竞争和死锁

一般来说,锁具有排他性。如果是共享锁(S锁),可以和另一个共享锁(S锁)同时拥有,但无法和一个排他锁(X锁)同时拥有;而对于一个X锁,则无法跟任何其他锁并发。当多个事务企图同时占用某一资源需要加锁时,就有可能发生锁竞争甚至死锁。

MySQL中的锁怎么理解

多个事务竞争同一资源

在上述案例中,三个事务依次请求对数据表加X锁,其中事务A成功请求,事务B和事务C会处于等待。当事务A提交事务后,虽然事务B和事务C处于同时竞争加锁状态,但由于MySQL对事务调度的FIFO(First In First Out,先入先出)特性,二者不会发生死锁,而是优先满足事务B加锁请求,待事务B提交事务后再满足事务C的加锁请求。

①,锁竞争+索引重复冲突造成死锁:

MySQL中的锁怎么理解

三个事务竞争资源存在索引重复

这个案例与锁竞争中的例子类似但又不同:假设事务A、事务B和事务C同时请求插入一条数据(插入语句都是加X锁),此时不仅仅是因为加锁冲突,还存在索引重复的问题,此时一旦事务A回滚释放锁后,事务B和事务C则会陷入死锁。这是一种特殊的死锁触发原因。

②,竞争同一资源出现死循环:

MySQL中的锁怎么理解

两个事务先竞争,后死锁

在这个案例中,先是事务A和事务B分别对id=1和id=2的记录加X锁,然后事务A继续对id=2的记录请求加锁时,因为该记录已被事务B占有,所以事务A只能等待;但此时事务B又企图对事务A已经占有的id=1记录加X锁,造成事务A和事务B在各自占有一定资源的基础上分别企图占用对方已加锁的资源,逻辑上冲突,骑虎难下,引擎不可能通过时间调度得以解决,故而发生死锁。

发生死锁后,引擎会根据相关的事务间的重要程度(包括占用资源多少、时间先后等)来选择一个进行回滚:例如上例中,事务A先于事务B请求加X锁,可将事务B看成是直接造成死锁的原因,所以选择对B进行回滚,而允许A加锁成功。

到此,相信大家对“MySQL中的锁怎么理解”有了更深的了解,不妨来实际操作一番吧!这里是亿速云网站,更多相关内容可以进入相关频道进行查询,关注我们,继续学习!

推荐阅读:
  1. 你需要理解的关于MySQL的锁知识
  2. 如何理解mysql innodb lock锁中的record lock

免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。

mysql

上一篇:Docker五种存储驱动原理及应用场景是怎样的

下一篇:如何进行SSH远程登录

相关阅读

您好,登录后才能下订单哦!

密码登录
登录注册
其他方式登录
点击 登录注册 即表示同意《亿速云用户服务条款》