锁在生活中处处可见,门锁,手机锁等等. 锁存在的意义是保护自己的东西不被别人偷走/修改. 在mysql中锁的意义也是一样,是为了保护自己的数据不被别人进行修改,从而导致出现脏读,幻读等问题.在学习锁的时候建议先学习一下mysql中的事务隔离级别以及底层索引B+树,以便更好的了解本文.
从图中可以看到mysql中的锁有很多种类型,不同的锁使用起来是不一样的.但他们都有一个共同的目的:(保护我方防御塔不被偷家).
从数据操作的粒度划分:行锁(间隙锁/临键锁) > 表级锁(元数据锁/意向锁) > 全局锁
为了尽可能提高数据库的并发度,每次锁定的数据范围越小越好
,锁的东西越多,则对数据库影响越大,性能也就越低.
锁的类型分类: 共享锁(也称读锁) > 排他锁(也称写锁)
共享锁针对同一份数据,多个事务的读操作可以同时进行而不会互相影响,相互不阻塞的。
排他锁:针对同一份数据, 当前写操作没有完成前,它会阻断
其他写锁和读锁。
全局锁就是对整个数据库实例加锁。加完锁以后整个库处于只读状态的时候,后续其他线程的以下语句都被阻塞:数据更新语句(数据的增删改)、数据定义语句(包括建表、修改表结构等)和更新类事务的提交语句.
加全局锁sql:
- Flush tables with read lock; //加锁
-
- unlock TABLES; //解锁
加全局锁的意义: 对整个数据库进行全局备份,需要保证数据的一致性,所以要加全局锁.
全局锁备份缺点: 在备份期间,由于加锁,导致正常业务无法进行更新.造成系统问题.
全局备份解决方法: 全局备份数据时,采用mysql自带的命令, mysqldump
mysqldump使用参数–single-transaction,导数据之前就会启动一个事务,来确保拿到一致性视图。
mysqldump –single-transaction -u账号 -p密码 数据库名 > 导出路径
该锁会锁定整张表,它是MySQL中最基本的锁策略,并不依赖于存储引擎,
由于表级锁一次会将整个表锁定,所以可以避免死锁
问题。表锁下面有两种锁策略(元数据锁/意向锁)
加表锁sql
- lock tables a write; //加锁
-
- unlock TABLES; //解锁
MDL加锁的过程是系统自动控制,无需显式使用,在访问一张表的时候会自动加上(增删改查都会自动加锁)。MDL锁的主要作用是维护表元数据的数据一致性,在表上有活动事务的时候,不可以对元数据进行写入操作。
查看元数据锁SQL
select object_type,object_schema,object_name,lock_type,lock_duration from `performance_schema`.metadata_locks;
默认情况下的锁查询,可以看出,元数据锁存放在mysql自带的数据库`performance_schema`.metadata_locks 中.
当我们访问其他表的时候 会自动加上元数据锁. 下图所示,当开启begin 然后查询表时,会自动加锁.
为什么要有意向锁这个概念呢.我们思考一个问题.
当执行update更新一条数据时,系统中是有一个行锁, 而此时又来了一个表锁的sql,这个表锁的sql需要判断一下该表中有没有其他锁来影响自己的加锁操作.于是就会从头到尾把所有数据全部排查一遍,来判断是否能加表锁.这个时候性能很慢的.
为了解决上面这个问题,所以mysql引入了意向锁这个概念,当有意向锁后,更新update一条数据时,会加两个锁, 一个是行锁,一个是意向锁. 当表锁的sql过来时,只需要判断该表是否有意向锁,而不需要遍历整个数据表.就可以进行加锁操作了.
InnoDB 支持多粒度锁,它允许行级锁与 表级锁 共存,而意向锁就是其中的一种表锁。
查看意向锁sql
select object_schema,object_name,index_name ,lock_type ,lock_mode from `performance_schema`.data_locks;
可以看出,元数据锁存放在mysql自带的数据库`performance_schema`.data_locks中.
当我们更新数据的时候 不仅会加上行锁,也会加上意向锁. 下图所示,当开启begin 然后查询表时,会自动加锁. IX(意向排他锁)
行级锁,每次操作锁住对应的行数据。锁定粒度最小,发生锁冲突的概率最低,并发度最高。应用在InnoDB存储引擎中(MYISAM不支持行锁)。
InnoDB的数据是基于索引组织的,行锁是通过对索引.上的索引项加锁来实现的,而不是对记录加的锁。对于行级锁,主要分为以下三类:
1.行锁 (Record Lock) :锁定单个行记录的锁,防止其他事务对此行进行update和delete。在RC、RR隔离级别下都支持。
2.间隙锁 (GapLock) :锁定索引记录间隙(不含该记录), 确保索引记录间隙不变,防止其他事务在这个间隙进行insert,产生幻读。在RR隔离级别下都支持。
3.临键锁 (Next-Key Lock) :行锁和间隙锁组合,同时锁住数据,井锁住数据前面的间隙Gap。在RR隔离级别下支持。
默认情况下,InnoDB在 RR事务隔离级别运行,InnoDB使 用临键锁 (Next-Key Lock)进行搜索和索引扫描,以防止幻读。
1. 针对唯一 索引进行检索时,对已存在的记录进行等值匹配时,将会自动优化为行锁。
2. InnoDB的行锁 是针对于索引加的锁,不通过索引条件检索数据,那么InnoDB将对表中的所有记录加锁,此时就会升级为表锁。
增删改查的加锁情况
查看行锁sql
select object_schema,object_name,index_name ,lock_type ,lock_mode from `performance_schema`.data_locks;
默认情况下,InnoDB在 RR事务隔离级别运行,InnoDB使用临键锁 (Next-Key Lock)进行搜索和索引扫描,以防止幻读。
1. 索引上的等值查询(唯一 索引),给不存在的记录加锁时,优化为间隙锁。
2. 索引上的等值查询(普通索引),向右遍历时最后一个值不满足查询需求时,next-key lock退化为间隙锁。 (如果是普通索引可能会出现多个重复的值,所以要使用间隙锁来锁定一个范围,而不是锁住邻居)
3.索引上的范围查询(唯一索引)会访问到不满足条件的第一个值为止。
注意:间隙锁唯一目的是防止其他事务插入间隙。间隙锁可以共存,一个事务采用的间隙锁不会阻止另一个事务在同一间隙上采用间隙锁。
查看间隙锁/临键锁sql
select object_schema,object_name,index_name ,lock_type ,lock_mode ,lock_data from `performance_schema`.data_locks;
这是表A的数据
图1 由于查询的id是3, 表中没有数据,所以加上了间隙锁
图2 由于查询的id是2, 表中存在数据,所以加上了临键锁