6.MySQL InnoDB 锁的基本类型

1.锁的粒度

1)行锁与表锁对比

1.锁的粒度:表锁,顾名思义,是锁住一张表;行锁就是锁住表里面的一行数据。锁定粒度,表锁肯定是大于行锁的。

2.加锁效率:表锁只需要直接锁住这张表就行了,而行锁,还需要在表里面去检索这一行数据,所以表锁的加锁效率更高。

3.冲突的概率:当我们锁住一张表的时候,其他任何一个事务都不能操作这张表。但是我们锁住了表里面的一行数据的时候,其他的事务还可以来操作表里面的其他没有被锁定的行,所以表锁的冲突概率更大。

4.并发性能:表锁的冲突概率更大,所以并发性能更低。

2. InnoDB 里面的基本的锁的模式

1)共享锁-行锁

1.定义:我们获取了一行数据的读锁以后,可以用来读取数据,所以它也叫做读锁。

2.注意点:1)注意不要在加上了读锁以后去写数据,不然的话可能会出现死锁的情况 2)多个事务可以共享一把读锁

3.如何加读锁?:我们可以用 select ...... lock in share mode; 的方式手工加上一把读锁。

4.释放锁的两种方式:只要事务结束,锁就会自动事务,包括提交事务和结束事务

2)排它锁-行锁

1.定义:它是用来操作数据的,所以又叫做写锁

2.注意点:只要一个事务获取了一行数据的排它锁,其他的事务就不能再获取这一行数据的共享锁和排它锁。

3.如何加锁:1)自动加排他锁。我们在操作数据的时候,包括增删改,都会默认加上一个排它锁。2)手工加锁,我们用一个 FOR UPDATE 给一行数据加上一个排它锁,这个无论是在我们的代码里面还是操作数据的工具里面,都比较常用。

4.释放锁的两种方式:只要事务结束,锁就会自动事务,包括提交事务和结束事务

5.排他锁的验证:

排他锁的验证

3)意向锁-表锁

1.定义:当我们给一行数据加上共享锁之前,数据库会自动在这张表上面加一个意向共享锁。当我们给一行数据加上排他锁之前,数据库会自动在这张表上面加一个意向排他锁。

2.如何实现:数据库自己维护的。自己手动无法实现

3.意义:1)有了意向锁这种表级别的锁,我们innodb中就可以支持更多粒度的锁。2)如果没有意向锁,,当我们准备给一张表加上表锁的时候,我们必须先要去判断有没其他的事务锁定了其中了某些行?如果有的话,肯定不能加上表锁,那么这个时候我们就要去扫描整张表才能确定能不能成功加上一个表锁,如果数据量特别大,比如有上千万的数据的时候,加表锁的效率就会很低!

4.本质:我们引入了意向锁之后,我只要判断这张表上面有没有意向锁,如果有,就直接返回失败。如果没有,就可以加锁成功。所以意向锁可以理解为一个标志,就像火车上厕所有没有人使用的灯,是用来提高加锁的效率的。

3.行锁的原理

1)锁到底锁住了什么呢?

InnoDB 的行锁,就是通过锁住索引来实现的。

2)为什么表里面没有索引的时候,锁住一行数据会导致锁表?

分析:当一张表,我们没有建立索引的时候,这里分成3种情况!1.如果我们定义了主键(PRIMARY KEY),那么 InnoDB 会选择主键作为聚集索引。2.如果没有显式定义主键,则 InnoDB 会选择第一个不包含有 NULL 值的唯一索引作为主键索引。3.如果也没有这样的唯一索引,则 InnoDB 会选择内置 6 字节长的 ROWID 作为隐藏的聚集索引,它会随着行记录的写入而主键递增。

结论:为什么锁表,是因为查询没有使用索引,会进行全表扫描,然后把每一个隐藏的聚集索引都锁住了。

3)为什么通过唯一索引给数据行加锁,主键索引也会被锁住?

分析:其实在上一节MySQL索引深入剖析的时候,已经分析过辅助索引它是怎么检索数据的!

索引检索数据流程

结论:我们通过辅助索引锁定一行数据的时候,它跟我们检索数据的步骤是一样的,会通过主键值找到主键索引,然后也锁定。

4 锁的算法

1)三种范围?

1.分析:因为我们用主键索引加锁,我们这里的划分标准就是主键索引的值。按照这个标准,我们分析3个范围!不同的范围对应不同的锁!我们锁的范围包括Record,Gap和Next-key,分别对应记录锁,间隙锁,临键锁。

锁的范围

2.主键索引不是整形,是字符怎么办呢?--> 用 ASCII 码来排序。

2)记录锁

1.定义:当我们对于唯一性的索引(包括唯一索引和主键索引)使用等值查询,精准匹配到一条记录的时候,这个时候使用的就是记录锁。比如whereid=1 4 7 10 。

2.特点:我们使用不同的 key 去加锁,不会冲突,它只锁住这个 record。

3)间隙锁

1.定义:当我们查询的记录不存在,没有命中任何一个 record,无论是用等值查询还是范围查询的时候,它使用的都是间隙锁。举个例子,where id >4 and id <7,where id = 6。

2.注意:1)间隙锁主要是阻塞插入 insert。相同的间隙锁之间不冲突 2)当查询的记录不存在的时候,使用间隙锁。

3.演示:

间隙锁的演示

4.如何关闭:Gap Lock 只在 RR 中存在。如果要关闭间隙锁,就是把事务隔离级别设置成 RC,并且把 innodb_locks_unsafe_for_binlog 设置为 ON。

4)临键锁

1.定义:当我们使用了范围查询,不仅仅命中了 Record 记录,还包含了 Gap间隙,在这种情况下我们使用的就是临键锁。临键锁=记录锁+间隙锁。它是 MySQL 里面默认的行锁算法!

2.退化的情况:1)唯一性索引,等值查询匹配到一条记录的时候,退化成记录锁。2)没有匹配到任何记录的时候,退化成间隙锁。

3.注意点:临键锁,锁住最后一个 key 的下一个左开右闭的区间。

4.举例:我们使用>5 <9, 它包含了记录不存在的区间,也包含了一个 Record 7。(接上面3种范围图)


Next-key范围
例图
例图

5.为什么要锁住下一个左开右闭的区间?----》就是为了解决幻读的问题。

5.死锁

1)锁的释放与阻塞?

1.问题:如果一个事务一直未释放锁,其他事务会被阻塞多久?在并发访问比较高的情况下,如果大量事务因无法立即获得所需的锁而挂起,会占用大量计算机资源,造成严重性能问题,甚至拖跨数据库。

2.查看:MySQL 有一个参数来控制获取锁的等待时间,默认是 50 秒。

查看等待时间

2)死锁的发生和检测

1.死锁的产生条件:(1)同一时刻只能有一个事务持有这把锁,(2)其他的事务需要在这个事务释放锁之后才能获取锁,而不可以强行剥夺,(3)当多个事务形成等待环路的时候,即发生死锁。

2.检测:InnoDB 一般都能通过算法(wait-for graph)自动检测到。一旦检测到死锁,就会退出事务,但不是百分之百!

3)查看锁信息(日志)

1.命令:

查看行锁信息
查看事务
查看当前的锁
查看锁等待

4)死锁的避免

1. 在程序中,操作多张表时,尽量以相同的顺序来访问(避免形成等待环路);

2.  批量操作单张表数据的时候,先对数据进行排序(避免形成等待环路);

3.申请足够级别的锁,如果要操作数据,就申请排它锁;

4.  尽量使用索引访问数据,避免没有 where 条件的操作,避免锁表;

5.如果可以,大事务化成小事务;

6.使用等值查询而不是范围查询查询数据,命中记录,避免间隙锁对并发的影响。

©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

友情链接更多精彩内容