ICode9

精准搜索请尝试: 精确搜索
首页 > 数据库> 文章详细

Mysql——7、MySQL锁

2021-10-15 10:04:15  阅读:160  来源: 互联网

标签:事务 加锁 行锁 间隙 MySQL 索引 InnoDB Mysql


1 MySQL锁

1.1 Mysql锁的类型

innoDB支持三种行锁定方式:

i. 行锁(Record Lock):锁直接加在索引记录上面(无索引项时演变成表锁)。

ii. 间隙锁(Gap Lock):锁定索引记录间隙,确保索引记录的间隙不变。间隙锁是针对事务隔离级别为可重复读或以上级别的。

iii. Next-Key Lock :行锁和间隙锁组合起来就是 Next-Key Lock。

1.2 mysql行锁

1.2.1 InnoDB行锁的使用条件

InnoDB支持表级锁,也支持行锁,默认是行锁

InnoDB行锁是通过给索引上的索引项加锁来实现的,只有通过索引条件检索数据,InnoDB才使用行级锁,否则,InnoDB将使用表锁

在不通过索引条件查询的时候,InnoDB确实使用的是表锁,而不是行锁。

1.2.2 InnoDB行锁的类型

InnoDB的行级锁定同样分为两种类型,共享锁和排他锁,而在锁定机制的实现过程中为了让行级锁定和表级锁定共存,InnoDB也同样使用了意向锁(表级锁定)的概念,也就有了意向共享锁和意向排他锁这两种。

如果一个事务请求的锁模式与当前的锁兼容,InnoDB就将请求的锁授予该事务;反之,如果两者不兼容,该事务就要等待锁释放。

四种锁的兼容方式如下:

img

意向锁是InnoDB自动加的,不需用户干预。

对于UPDATE、DELETE和INSERT语句,InnoDB会自动给涉及数据集加排他锁(X);对于普通SELECT语句,InnoDB不会加任何锁;事务可以通过以下语句显示给记录集加共享锁或排他锁。

共享锁(S):SELECT * FROM table_name WHERE ... LOCK IN SHARE MODE

排他锁(X):SELECT * FROM table_name WHERE ... FOR UPDATE

用SELECT ... IN SHARE MODE获得共享锁,主要用在需要数据依存关系时,来确认某行记录是否存在,并确保没有人对这个记录进行UPDATE或者DELETE操作。

  但是如果当前事务也需要对该记录进行更新操作,则很有可能造成死锁,对于锁定行记录后需要进行更新操作的应用,应该使用SELECT... FOR UPDATE方式获得排他锁。

1.2.3 InnoDB行锁使用过程中会出现的问题

1、 访问不同的行的记录,但使用相同索引键,会出现锁冲突

2、 Mysql发现全表查找比索引查找效率高,就会使用全表查询,就会使用表锁,而不是行锁

看似命中索引,走行锁,结果却是表锁,最终导致锁等待情况

理解:

由于MySQL的行锁是针对索引加的锁,不是针对记录加的锁,所以虽然是访问不同行的记录,但是如果是使用相同的索引键,是会出现锁冲突的。

当表有多个索引的时候,不同的事务可以使用不同的索引锁定不同的行,另外,不论是使用主键索引、唯一索引或普通索引,InnoDB都会使用行锁来对数据加锁。

即便在条件中使用了索引字段,但是否使用索引来检索数据,是由MySQL通过判断不同执行计划的代价来决定的

如果MySQL认为全表扫描效率更高,比如对一些很小的表,它就不会使用索引,这种情况下InnoDB将使用表锁,而不是行锁。因此,在分析锁冲突时,别忘了检查SQL的执行计划,以确认是否真正使用了索引。

1.2.4 InnoDB行锁使用过程中问题场景

1.2.4.1 对没有索引的加锁,导致表锁

示例:

1)准备工作:建tab_no_index表,表中无任何索引,并插入数据

img img

2)Session_1: 我们给id=1的行加上排它锁(for update),由于id没有索引,实际上是表级锁;

img

3)Session_2:我们给id=2的行加上排它锁(for update),由于id没有索引,所以去申请表级锁,但是却出现了锁等待!原因就是在没有索引的情况下,InnoDB只能使用表锁。

img

备注:MySQL中的for update 仅适用于InnoDB(因为是只有此引擎才有行级锁),并且必须开启事务,在begin与commit之间才生效。

for update是在数据库中上锁用的,可以为数据库中的行上一个排它锁。当一个事务的操作未完成时候,其他事务可以对这行读取但是不能写入或更新,只能等该事务Rollback, Commit, Lost connection…

1.2.4.2 对有索引的键值加锁,会对所有涉及到的数据行加锁

1)准备工作:对id建索引如下

2)Session_1:此时id是有索引的,我们对id=1 and name=1的一行加排它锁;

img

3)Session_2:访问不同于Session_1的id=1, name=5行,但是索引键值是一样的,照样等待锁,锁冲突了。

img

1.2.4.3 多个索引时,不同的事务可以使用不同的索引锁定不同的行

不论什么索引,InnoDB都会使用行锁对数据加锁(对有索引的行数据)。

1)准备工作:对tab_no_index追加name索引:alter table tab_no_index add index name(name);

2)Session_1:开启事务对id=1的行加排它锁,即对name=1与name=5两个数据加锁。

img

3)Session_2:开启事务对name=2行加锁,因为该数据没有被加锁,索引可以获得锁

img

4)Session_3:再对name=5的数据进行加锁,由于该数据记录已被Session_1锁定,所以等待获得锁。

img

1.3 Mysql间隙锁

1.3.1 为什么要使用next-key锁(众安)

innoDB的间隙锁只存在于 RR 隔离级别。间隙锁在innoDB中的唯一作用就是在一定的“间隙”内防止其他事务的插入操作,以此防止幻读的发生:

a) 防止间隙内有新数据被插入。

b) 防止已存在的数据,更新成间隙内的数据。

1.3.2 Mysql间隙锁的加锁操作

innoDB默认的隔离级别是可重复读(Repeatable Read),并且会以Next-Key Lock的方式对数据行进行加锁。

Next-Key Lock是行锁和间隙锁的组合,当InnoDB扫描索引记录的时候,会首先对索引记录加上行锁(Record Lock),再对索引记录两边的间隙加上间隙锁(Gap Lock)。加上间隙锁之后,其他事务就不能在这个间隙修改或者插入记录。

当查询的索引含有唯一属性(唯一索引,主键索引)时,Innodb存储引擎会对next-key lock进行优化,将其降为record lock,即仅锁住索引本身,而不是范围。

mysql的隔离级别,可以防止脏读,不可重复读,但不能防止虚读或幻读,即能防止读取未提交的数据,能防止读取已经提交的数据,但不能防止读取已经插入的数据,所以,需要引入间隙锁。

在进行当前读的情况下,对读出的数据的附近的一整个范围(“间隙”)进行加锁,保证满足查询条件的记录不能被插入。

img

四级隔离级别中,只有在可串行化的级别之下,才可以防止幻读的出现。事务A执行过程中,由于事务B并发插入了一条新数据,事务A两次读数据的内容不一样,出现了“虚幻”的新纪录

1.3.3 Mysql如何防止幻读

MySQL的innoDB引擎在RR级别,不能防止幻读产生,而为了防止幻读的发生,有以下两种措施:

\1) 在快照读(snapshot read)的情况下,MySQL通过MVCC(多版本并发控制)来避免幻读。

快照读,读取的是记录的可见版本 (有可能是历史版本),不用加锁。

主要应用于无需加锁的普通查询(select)操作。

\2) 在当前读(current read)的情况下,MySQL通过next-key lock来避免幻读。

当前读,读取的是记录的最新版本,并且会对当前记录加锁,防止其他事务并发修改这条记录。

加行共享锁(SELECT ... LOCK IN SHARE MODE )、加行排他锁(SELECT ... FOR UPDATE / INSERT / UPDATE / DELETE)的操作都会用到当前度。

1.3.4 什么情况下使用间隙锁

1、 范围条件查询中范围内的数据

当用范围条件,而不是相等条件检索数据,并请求共享或者排它锁的时候,InnoDB会给符合条件的已有数据记录的索引项加锁;

2、 不在范围内,且并不存在的

对于不在范围内的但并不存在的记录,叫做“间隙(GAP)”,InnoDB也会对这个间隙加锁,这就是所谓的间隙锁。

理解:

1、 如:select * from where id>100 for update

对id大于100的数据对加锁,但是此时数据中id只有1,2….100,101,不仅对存在的101的记录加锁,还会对大于101不存在的数据的间隙加锁。

2、 此外,对使用相等条件请求给一个不存在的记录加锁,InnoDB也会使用间隙锁,如下:

Session_1:对不存在的id=6的记录加锁

img

Session_2:插入id=6的记录,也会出现锁等待

img

1.3.5 何时使用行锁,何时产生间隙锁

1、只使用唯一索引查询,并且只锁定一条记录时,innoDB会使用行锁。

2、只使用唯一索引查询,但是检索条件是范围检索,或者是唯一检索,然而检索结果不存在(试图锁住不存在的数据)时,会产生 Next-Key Lock。

3、使用普通索引检索时,不管是何种查询,只要加锁,都会产生间隙锁。

4、同时使用唯一索引和普通索引时,由于数据行是优先根据普通索引排序,再根据唯一索引排序,所以也会产生间隙锁。

1.4 innodb是怎么实现可重复读的(众安)

通过一致性视图和多版本并发控制(MVCC)实现。

理解:

Repeatable Read(可重复读):一个事务在执行过程中可以看到其他事务已经提交的新插入的记录(读已经提交的,其实是读早于本事务开始且已经提交的),但是不能看到其他事务对已有记录的更新(即晚于本事务开始的),并且,该事务不要求与其他事务是“可串行化”的

RR隔离级别是怎么保证这一点的呢?

通过一致性视图和多版本并发控制(MVCC)实现。

使用MVCC(多版本并发控制)。InnoDB为每行记录添加了一个版本号(系统版本号),每当修改数据时,版本号加一。

在读取事务开始时,系统会给事务一个当前版本号,事务会读取版本号<=当前版本号的数据,这时就算另一个事务插入一个数据,并立马提交,新插入这条数据的版本号会比读取事务的版本号高,因此读取事务读的数据还是不会变。

如果数据库并发控制引擎是单纯的封锁协议机制,则应该在读取数据的时候,判断数据项是不是其他事务更新过的。可是InnoDB没有这么做,而是通过如下方式,在RR隔离级别下为事务设置了一个“一致性读视图(即快照)”,之后读取数据,就是根据这个快照来获取,这样,就不能看到他晚于本事务的事务对已有记录的更新(更新生成新版本,必然不在旧的快照所限定的范围内)

1.5 mysql的隔离级别

mysql是如何实现可重复读的:通过mvcc多版本并发控制实现的,也就说是会在事物开始的时候,创建快照,之后查询就用这个快照进行,后续事物的更改对这个快照是不可见的,这就实现了可重复读,

mysql实现读已提交,每一个语句执行前,都会重新计算一个新的视图

快照在mvcc中是怎么工作的:innodb中每个事物都会有一个自己的事物id,并且是唯一的,递增的,

标签:事务,加锁,行锁,间隙,MySQL,索引,InnoDB,Mysql
来源: https://www.cnblogs.com/askfb/p/15409800.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有