概述
MySQL中的也存在一些类型的锁,用来保证多个连接同时操作数据时的安全即数据的一致性问题;同时,虽然锁能够解决一些数据的一致性和有效性,但是我们还是要选择合适的锁来降低锁对于并发问题的影响
1. 全局锁
全局锁就是对整个库进行加锁,所有的连接都只能进行读的操作,不能写(DDL、DML、以及更新操作的事务的提交语句都会被阻塞)
最典型的一个例子就是使用mysqldump命令对库进行备份的时候,将会锁定所有的表,从而保证数据的一致性
思考:假设没有全局锁,使用表锁,会出现什么情况?
- 假设有A、B三张表,一个完整的业务操作过来需要依次操作两张表
- 首先开始备份数据,开始备份A,锁住了表A;此时业务操作过来了由于A表被锁定无法写入数据;待A表备份完成后,执行了操作,修改了表A和表B
- 表B开始备份,锁定了B
我们可以发现,备份出来的A表缺少了这次操作的记录,而备份出来的B表中是包含这次操作的记录的;这就违背了数据的一致性;解决办法就是加上全局锁,锁住所有的表,保证所有的表只能被读而不能被写入
全局锁语法
# 加锁
flush tables with read lock;
# 解锁
unlock tables;
# 备份库的数据,在mysql外部执行;-h如果备份远程mysql服务需要加上参数host,schemal_name是表名,file_url + file_name是备份到本地的什么路径和文件名,--single-transation仅支持innodb引擎不添加全局锁,而是通过快照读保证数据的一致性
mysqldump [-h127.0.0.1] [--single-transaction] -uroot -p1234 schemal_name > file_url + file_name;
全局锁特点:数据库中添加全局锁是一个非常重量级的操作,存在以下问题:
- 如果在主库上备份就会导致所有写的操作无法执行,业务基本上都要停掉
- 如果在从库上备份,则备份期间不能执行主库同步过来的二进制日志(binlog),导致主从延迟
为了解决备份期间加全局锁的问题,Innodb引擎我们可以在备份的时候加上参数
--single-transaction
来完成不加锁的一致性数据备份,使用方式如上图
备份时不加全局锁的示意图:
2. 表级锁
表级锁每次操作锁住的是一张表,锁的粒度较大,并发程度较低。应用在Innodb、myisam引擎中
表级锁可以分为三种:表锁、元数据锁、意向锁
1. 表锁
表锁可以分为两类:表共享读锁 read和表独占写锁 write
假设有两个连接:A和B
表共享读锁:
SHARE_READ_ONLY
连接A在表上添加了共享读锁之后,A和B都只能读,不能写,所以叫做共享读
表独占写锁:
SHARE_NO_READ_WRITE
连接A在表上添加了独占写锁之后,A可以读也可以写,B什么都做不了,相当于A把表给独占了,所以叫独占写
语法:
# 加读锁或者写锁
lock tables 表名 read|write;
# 释放锁;断开客户端的连接也会导致释放锁
unlock tables;
读写锁:
2. 元数据锁 (meta data lock, MDL)
元数据锁存在的目的是为了:当有活动的事务存在的时候,锁住表的结构,防止修改表导致事务提交失败
这个锁是由程序自动控制的,无需手动添加;可以通过下面的sql查看元数据锁的添加情况
元数据锁有两种:MDL读锁
、MDL写锁
# 查看元数据记录是否开启
select * from performance_schema.setup_instruments where NAME ='wait/lock/metadata/sql/mdl';
# 开启元数据锁记录,当发生锁的情况时可以通过下方语句查看
UPDATE performance_schema.setup_instruments SET ENABLED = 'YES' WHERE NAME ='wait/lock/metadata/sql/mdl';
# 查看元数据锁,从performance_schema库中的meta_data_lock查看
select object_type, object_schema, object_name, lock_type, lock_duration from performance_schema.metadata_locks;
这个不需要过多的分析,只要该表上有其他锁存在,就无法修改表结构
3. 意向锁
由于InnoDB引擎中进行查询如果能够使用到行锁,是会对数据加上对应的行锁,同时添加一个意向锁,这样子当另一个事务过来的时候,就不需要再去遍历索引查看有没有行锁,直接通过意向锁来进行判断是否可以执行
意向锁有两种;**意向共享锁
IS
和意向排他锁IX
**两种
- 意向共享锁和意向排他锁之间是互相兼容的,否则没必要上意向锁直接用表锁就可以了;
- 意向共享锁兼容表共享读,不兼容表独占写;意向排他锁和其他都互斥
sql语句对应加锁类型
- select … lock in share加的意向共享锁
- insert、update、delete、select … fro update加**意向排他锁 **
可以通过下面sql查看意向锁加锁情况
# 查询performance_schema库中的data_lock,意向锁和行级锁都在这里
select object_schema, object_name, index_name, lock_type, lock_mode, lock_data from performance_schema.data_locks;
3. 行级锁
行级锁每次操作是锁住对应的记录,锁的粒度最小,并发程度最高,应用在Innodb引擎中
行级锁的实现基于索引,通过对索引项加锁来进行操作,不是锁住的记录,因此如果索引失效就不会应用到行级锁
行级锁主要分为三类:
- 行锁:
Record Lock
,锁定单行记录,防止其他事务对此行进行udpate和delete。在RC、RR隔离级别下都支持- 间隙锁:
Gap Lock
,锁定索引记录间隙,不包含该记录,确保索引间隙不变,防止其他事务在这个间隙insert产生幻读。在RR隔离级别下支持- 临键锁:
Next Key Lock
,行锁和间隙锁组合,锁住数据和数据前面的间隙Gap。在RR隔离级别下支持不同SQL添加的锁的类型
- 普通select:不加锁
- select … lock in share mode:共享锁
- insert、update、delete、select … for update:排他锁
可以通过下面sql查看加所情况
# 查询performance_schema库中的data_lock,意向锁和行级锁都在这里
select object_schema, object_name, index_name, lock_type, lock_mode, lock_data from performance_schema.data_locks;
1. 行锁
默认情况下Innodb使用可重复读RR的隔离级别,Innodb使用next-key锁进行搜索和索引扫描防止出现幻读
- 针对唯一索引,对已存在的纪录进行等值匹配时会自动优化为行锁
- 行级锁基于索引,如果使用过程中没有用到索引或者索引失效则升级为表锁
2. 间隙锁/临键锁
- 间隙锁存在的目的就是锁住间隙防止插入数据产生幻行;间隙锁可以共存,一个事务不会阻挡另一给事务在同一个间隙上加锁
- 针对唯一索引的等值查询,给不存在的记录加锁时临键锁优化为间隙锁
- 针对普通索引的等值查询,向右遍历时最后一个值不满足查询需求时,临键锁退化为间隙锁
- 针对唯一索引的等值查询,会访问到不满足条件的第一个值为止
间隙锁可以共存,一个事务不会阻挡另一给事务在同一个间隙上加锁
2. 针对唯一索引的等值查询,给不存在的记录加锁时临键锁优化为间隙锁
3. 针对普通索引的等值查询,向右遍历时最后一个值不满足查询需求时,临键锁退化为间隙锁
4. 针对唯一索引的等值查询,会访问到不满足条件的第一个值为止