第七章 MySQL 锁机制 *
在MySQL中,就很容易出现多线程同时操作表中数据的情况,如果要避免潜在的并发问题,那么我们可以使用之前讲解的事务隔离级别来处理,而事务隔离中利用了锁机制。
- 读未提交(Read Uncommitted):能够读取到其他事务中未提交的内容,存在脏读问题。
- 读已提交(Read Committed RC):只能读取其他事务已经提交的内容,存在不可重复读问题。
- 可重复读(Repeated Read RR):在读取某行后不允许其他事务操作此行,直到事务结束,但是依然存在幻读问题。
- 串行读(Serializable):一个事务的开始必须等待另一个事务的完成。
我们可以切换隔离级别分别演示一下:
|
|
在RR级别下,MySQL在一定程度上解决了幻读问题:
- 在快照读(不加锁)读情况下,MySQL 通过 MVCC 来避免幻读。
- 在当前读(加锁)读情况下,MySQL 通过next-key来避免幻读。
MVCC
,全称Multi-Version Concurrency Control
,即多版本并发控制。MVCC 是一种并发控制的方法,一般在数据库管理系统中,实现对数据库的并发访问,在编程语言中实现事务内存。
第一节 读锁和写锁
从对数据的操作类型上来说,锁分为读锁和写锁:
- **读锁:**也叫共享锁,当一个事务添加了读锁后,其他的事务也可以添加读锁或是读取数据,但是不能进行写操作,只能等到所有的读锁全部释放。
- **写锁:**也叫排他锁,当一个事务添加了写锁后,其他事务不能读不能写也不能添加任何锁,只能等待当前事务释放锁。
第二节 全局锁、表锁和行锁
从锁的作用范围上划分,分为全局锁、表锁和行锁:
- **全局锁:**锁作用于全局,整个数据库的所有操作全部受到锁限制。
- **表锁:**锁作用于整个表,所有对表的操作都会收到锁限制。
- **行锁:**锁作用于表中的某一行,只会通过锁限制对某一行的操作(仅InnoDB支持)
1. 全局锁
我们首先来看全局锁,它作用于整个数据库,我们可以使用以下命令来开启读全局锁:
|
|
开启后,整个数据库被上读锁,我们只能去读取数据,但是不允许进行写操作(包括更新、插入、删除等)一旦执行写操作,会被阻塞,直到锁被释放,我们可以使用以下命令来解锁:
|
|
除了手动释放锁之外,当我们的会话结束后,锁也会被自动释放。
2. 表锁
表锁作用于某一张表,也是MyISAM和InnoDB存储引擎支持的方式,我们可以使用以下命令来为表添加锁:
|
|
在我们为表添加写锁后,我们发现其他地方是无法访问此表的,一律都被阻塞。
3. 行锁
表锁的作用范围太广了,如果我们仅仅只是对某一行进行操作,那么大可不必对整个表进行加锁,因此InnoDB
支持了行锁,我们可以使用以下命令来对某一行进行加锁:
|
|
使用InnoDB的情况下,在执行更新、删除、插入操作时,数据库也会自动为所涉及的行添加写锁(排他锁),直到事务提交时,才会释放锁,执行普通的查询操作时,不会添加任何锁。使用MyISAM的情况下,在执行更新、删除、插入操作时,数据库会对涉及的表添加写锁,在执行查询操作时,数据库会对涉及的表添加读锁。
**提问:**当我们不使用索引列进行选择,行锁会发生什么变化?(行锁升级)
如果没有走索引,引擎就无法得知SELECT语句到查询的是哪一行不,行锁就会升级为全表锁(本质为记录锁)。
第三节 记录锁、间隙锁和临键锁
我们知道InnoDB支持使用行锁,但是行锁比较复杂,它可以继续分为多个类型。
1. 记录锁
(Record Locks)记录锁, 仅仅锁住索引记录的一行,在单条索引记录上加锁。Record lock锁住的永远是索引,而非记录本身,即使该表上没有任何索引,那么 InnodDB 会在后台创建一个隐藏的聚集主键索引,那么锁住的就是这个隐藏的聚集主键索引。所以说当一条sql没有走任何索引时,那么将会在每一条聚合索引后面加写锁,这个类似于表锁,但原理上和表锁应该是完全不同的。
2. 间隙锁
(Gap Locks)仅仅锁住一个索引区间(开区间,不包括双端端点)。在索引记录之间的间隙中加锁,或者是在某一条索引记录之前或者之后加锁,并不包括该索引记录本身。比如在 1、2中,间隙锁的可能值有 (-∞, 1),(1, 2),(2, +∞),间隙锁可用于防止幻读,保证索引间的不会被插入数据。
3. 临键锁
(Next-Key Locks)Record lock + Gap lock,左开右闭区间。默认情况下,InnoDB
正是使用Next-key Locks来锁定记录(如select … for update语句)它还会根据场景进行灵活变换:
场景 | 转换 |
---|---|
使用唯一索引进行精确匹配,但表中不存在记录 | 自动转换为 Gap Locks |
使用唯一索引进行精确匹配,且表中存在记录 | 自动转换为 Record Locks |
使用非唯一索引进行精确匹配 | 不转换 |
使用唯一索引进行范围匹配 | 不转换,但是只锁上界,不锁下界 |
注:关于更多临键锁机制可以阅读此文章 MySQL的锁机制 - 记录锁、间隙锁、临键锁 - 知乎