目录
一、概述
锁机制用来保证多线程(同一事务、不同事务之间)并发访问同一行数据引发的数据一致性问题。
MySQL的锁分为以下三类:
- 全局锁:线程串行访问数据库
- 表级锁:在一张表上加锁,线程串行访问该表
- 行级锁:在一行记录上加锁,每次操作锁住对应的行数据
二、全局锁
对整个数据库实例加锁,加锁后其他线程可以读数据库内容,但是创建表、增删改操作会被阻塞。
全局锁一般用于备份整个数据库时 使用,防止涉及多表更新的业务 在备份文件中出现数据不一致问题。

数据库备份过程:
- 添加全局锁:flush tables with read lock;
- 执行备份:mysqldump -h服务器IP地址 -u用户名 -p密码 数据库名 > 备份保存绝对路径D:/.../xxx.sql
- 释放锁:unlock tables;
备份后数据会发生变化,所以可以参考redis将备份后执行的MySQL指令记录下来,便于数据库宕机后使用指令日志和备份文件恢复数据。
全局锁对于备份主从数据库存在的问题:
- 备份主库则写操作相关的业务会全部阻塞。
- 备份从库则备份期间无法执行主库同步来的数据,导致主从延迟。
在InnoDB中通过添加-single-transaction参数可以实现不加锁的一致性数据备份。(原理是快照,应该是MVCC的内容)
三、表级锁
表级锁分为三类,本质都是基于读写锁:
- 表锁 :对整张表加锁,解决DML之间的并发冲突
- 元数据锁MDL :解决DML和DDL的并发冲突
- 意向锁 :解决表锁和行锁的冲突
1.表锁
表锁又分为read lock和write lock两类。这里和缓存更新的读写锁原理一样。
- 加锁:
lock tables 表名... read/wirte; - 释放锁:
unlock tables;持有锁的线程断开连接会自动释放锁。
1.1 read lock
表共享读锁,又叫读锁,共享锁。执行读操作业务的线程申请读锁或不加锁 ,可以被多个读线程同时持有,持有锁的线程可以对该表进行读操作,但所有线程不能进行写操作 。
如果有多个线程都持有同一个表的读锁,那么只要还有一个线程持有读锁,写操作就会被阻塞,直到所有读线程都释放锁,因此写线程会有饥饿问题。

- 在任何隔离级别下,select lock in share mode、update、delete、insert操作,共享锁和排它锁在事务提交时释放。
- 在serializable隔离级别下,普通的select操作会自动添加lock in share mode后缀。
- 在读未提交、读已提交、可重复读隔离级别下,普通的select操作不会加锁。
1.2 write lock
表独占写锁,又叫写锁,排它锁,独占锁。执行写操作业务的线程申请写锁,写锁只能由一个线程持有,期间其他线程的增删改查都会被阻塞,直到写线程释放锁。
2.元数据锁
元数据锁是为了防止DML和DDL冲突。
MySQL会为执行增删查改操作的表自动加元数据共享锁 ,期间不允许表结构被修改,直到所有线程都不持有共享锁。当要alter table 修改表结构时会自动对表加元数据排它锁,期间不允许任何的增删查改操作。
3.意向锁
意向锁是为了解决行锁与表锁的冲突,加表锁前不必逐行遍历判断是否有行锁,直接判断意向锁即可。
读线程在加行锁时 会额外将意向锁设为 意向共享锁,写线程在加行锁时 会额外将意向锁设为 意向排它锁。
读线程加表锁前判断意向锁是共享锁,那么可以加表锁。意向锁为空时也可以加表锁。其他任何情况都不能加表锁。
四、行级锁
因为InnoDB中的数据存储结构是索引,所以行锁是对记录在索引B+树上的索引项加锁,而不是对记录加锁。
-
行锁:锁定单个索引项 ,防止其他线程对该项进行update和delete,应用在读已提交、可重复读 事务隔离级别。

-
间隙锁:锁定索引项之间的间隙 ,确保索引记录之间的间隙不变,防止其他线程在间隙进行insert,解决幻读 ,应用在可重复读 事务隔离级别。

-
临键锁:同时锁定单个索引项和索引项前的间隙 ,应用在可重复读 事务隔离级别。

1.行锁
行锁实现还是读锁和写锁。在InnoDB中,对insert、update、delete加排它锁,对select不加锁。 其中,排它锁解决了脏读问题,MVCC解决了不可重复读和幻读问题。
2.临键锁
InnoDB在可重复读 事务隔离级别下使用的是临键锁防止幻读。
- SQL执行时若针对unique索引 的B+树进行搜索且是等值匹配 时会降级为行锁。
- SQL执行时若针对unique索引 的B+树进行搜索且是等值匹配 ,且索引项不存在 时会降级为间隙锁。例如索引项1,2,4,5,此时对索引项3进行增删改会先将2,4之间的间隙上锁。
- SQL执行时若针对非unique索引 的B+树进行搜索且是等值匹配 ,且索引项不存在 时会降级为间隙锁。例如索引项1,2,2,4,5,此时对索引项3进行增删改会先将2,4之间的间隙上锁。
- SQL执行时若不走任何索引,那么会升级为表锁。