前面我们通过查询语句,更新语句了解了 MySQL 的 innodb 的内部结构以及执行的流程,之后我们讲了有关索引的内容,通过介绍 innodb 的逻辑存储结构和 B+Tree的模型推演来引出索引的概念,并阐述了我们如何通过我们对索引的了解对 MySQL 进行性能优化;再到后面,我们来到了事务的篇章,我们分别讲了事务的四大特性以及隔离级别,至于关于隔离级别的实现,我们总共有两种实现方案,LBCC 以及 MVCC,在上一篇章中,我们着重讲了 MVCC 的相关介绍,关于锁的方面,我们且看这一篇章。
老规矩,先上飞机票:
前面提到的脑图如下,想要完整高清图片可以到微信我的公众号下【6曦轩】下回复 MySQL 脑图获取:
https://dev.mysql.com/doc/refman/5.7/en/innodb-locking.html
官网把锁分成了 8 类。所以我们把前面的两个行级别的锁(Shared and Exclusive Locks),和两个表级别的锁(Intention Locks)称为锁的基本模式。
后面三个 Record Locks、Gap Locks、Next-Key Locks,我们把它们叫做锁的算法,也就是分别在什么情况下锁定什么范围。
我们讲到 InnoDB 里面既有行级别的锁,又有表级别的锁,我们先来分析一下这两种锁定粒度的一些差异。
表锁,顾名思义,是锁住一张表;行锁就是锁住表里面的一行数据。锁定粒度,表锁肯定是大于行锁的。
大于。
为什么?
表锁只需要直接锁住这张表就行了,而行锁,还需要在表里面去检索这一行数据,所以表锁的加锁效率更高。
大于,因为当我们锁住一张表的时候,其他任何一个事务都不能操作这张表。但是我们锁住了表里面的一行数据的时候,其他的事务还可以来操作表里面的其他没有被锁定的行,所以表锁的冲突概率更大。
表锁的冲突概率更大,所以并发性能更低,这里并发性能就是小于。
InnoDB 里面我们知道它既支持表锁又支持行锁,另一个常用的存储引擎 MyISAM 支持什么粒度的锁?这是第一个问题。第二个就是 InnoDB 已经支持行锁了,那么它也可以通过把表里面的每一行都锁住来实现表锁,为什么还要提供表锁呢?
要搞清楚这个问题,我们就要来了解一下 InnoDB 里面的基本的锁的模式(lock mode),这里面有两个行锁和两个表锁。
第一个行级别的锁就是我们在官网看到的 Shared Locks (共享锁),我们获取了一行数据的读锁以后,可以用来读取数据,所以它也叫做读锁,注意不要在加上了读锁以后去写数据,不然的话可能会出现死锁的情况。而且多个事务可以共享一把读锁。那怎么给一行数据加上读锁呢?
我们可以用 select …… lock in share mode; 的方式手工加上一把读锁。
释放锁有两种方式,只要事务结束,锁就会自动事务,包括提交事务和结束事务。
我们也来验证一下,看看共享锁是不是可以重复获取。
Transaction 1 | Transaction 2 |
---|---|
begin; | |
SELECT * FROM student WHERE id=1 LOCK IN SHARE MODE; | |
- | begin; |
- | SELECT * FROM student WHERE id=1 LOCK IN SHARE MODE;// OK |
第二个行级别的锁叫做 Exclusive Locks(排它锁),它是用来操作数据的,所以又叫做写锁。只要一个事务获取了一行数据的排它锁,其他的事务就不能再获取这一行数据的共享锁和排它锁。
排它锁的加锁方式有两种,第一种是自动加排他锁。我们在操作数据的时候,包括增删改,都会默认加上一个排它锁。
还有一种是手工加锁,我们用一个 FOR UPDATE 给一行数据加上一个排它锁,这个无论是在我们的代码里面还是操作数据的工具里面,都比较常用。
释放锁的方式跟前面是一样的。
排他锁的验证:
Transaction 1 | Transaction 2 |
---|---|
begin; | |
UPDATE student SET sname = ‘猫老公 555’ WHERE id=1; | |
- | begin; |
- | SELECT * FROM student WHERE id=1 LOCK IN SHARE MODE; // BLOCKED |
- | SELECT * FROM student where id=1 FOR UPDATE; // BLOCKED |
- | DELETE FROM student where id=1 ; // BLOCKED |
这个是两个行锁,接下来就是两个表锁。
意向锁是什么呢?我们似乎从来没有听过,也从来没有使用过,其实他们是由数据库自己维护的。
也就是说,当我们给一行数据加上共享锁之前,数据库会自动在这张表上面加一个意向共享锁。
当我们给一行数据加上排他锁之前,数据库会自动在这张表上面加一个意向排他锁。
反过来说:
如果一张表上面至少有一个意向共享锁,说明有其他的事务给其中的某些数据行加上了共享锁。
如果一张表上面至少有一个意向排他锁,说明有其他的事务给其中的某些数据行加上了排他锁。
select * from t2 where id =4 for update;
TABLE LOCK table example
.t2
trx id 24467 lock mode IX
RECORD LOCKS space id 64 page no 3 n bits 72 index PRIMARY of table example
.t2
trx id 24467 lock_mode X locks rec but not gap
那么这两个表级别的锁存在的意义是什么呢?第一个,我们有了表级别的锁,在InnoDB 里面就可以支持更多粒度的锁。它的第二个作用,我们想一下,如果说没有意向锁的话,当我们准备给一张表加上表锁的时候,我们首先要做什么?是不是必须先要去判断有没其他的事务锁定了其中了某些行?如果有的话,肯定不能加上表锁。那么这个时候我们就要去扫描整张表才能确定能不能成功加上一个表锁,如果数据量特别大,比如有上千万的数据的时候,加表锁的效率是不是很低?
但是我们引入了意向锁之后就不一样了。我只要判断这张表上面有没有意向锁,如果有,就直接返回失败。如果没有,就可以加锁成功。所以 InnoDB 里面的表锁,我们可以把它理解成一个标志。就像火车上厕所有没有人使用的灯,是用来提高加锁的效率的。
Transaction 1 | Transaction 2 |
---|---|
begin; | |
SELECT * FROM student where id=1 FOR UPDATE; | |
- | BEGIN; |
- | LOCK TABLES student WRITE; // BLOCKED UNLOCK TABLES; // 释放表锁的方式 |
以上就是 MySQL 里面的 4 种基本的锁的模式,或者叫做锁的类型。
有问题?可以给我留言或私聊
有收获?那就顺手点个赞呗~
当然,也可以到我的公众号下「6曦轩」,
回复“学习”,即可领取一份
【Java工程师进阶架构师的视频教程】~
回复“面试”,可以获得:
【本人呕心沥血整理的 Java 面试题】
回复“MySQL脑图”,可以获得
【MySQL 知识点梳理高清脑图】
由于我咧,科班出身的程序员,php,Android以及硬件方面都做过,不过最后还是选择专注于做 Java,所以有啥问题可以到公众号提问讨论(技术情感倾诉都可以哈哈哈),看到的话会尽快回复,希望可以跟大家共同学习进步,关于服务端架构,Java 核心知识解析,职业生涯,面试总结等文章会不定期坚持推送输出,欢迎大家关注~~~