MySQL 中锁详解(行锁、表锁、意向锁、间隙锁、临键锁、共享锁、排他锁)

1、简介

        在数据库系统中,当某个用户在修改一部分数据时,MySQL会通过锁定防止其他用户读取同一数据。在处理并发读或写时,通过实现一个由两种类型的锁组成的锁系统来解决。两种锁通常被称为共享锁(sharedlock)和排他锁(exclusivelock),也叫读锁(readlock)和写锁( writelock )。读锁是共享的,是互相不阻塞的。多个客户端在同一时刻可以同时读取同一个资源,而不互相干扰。写锁则是排他的,也就是说一个写锁会阻塞其他的写锁和读锁,这是出于安全策略的考虑,只有这样才能 确保在给定的时间里,只有一个用户能执行写入,并防止其他用户读取正在写入的同一资源。

2、锁分类
2.1、按粒度分

全局锁:全局锁就是对整个数据库实例加锁,加锁后整个实例就处于只读状态,后续的DML的写语句,DDL语句,已经更新操作的事务提交语句都将被阻塞。

# 1、对整个数据库上锁
flush tables with read lock;
# 2、释放锁
unlock tables;

注当我们进行数据库备份时经常用到,但是在业务场景中,阻塞更新是不能接受的,因此在备份时如果不加锁,可以在导出数据文件命令中添加 --single-transaction 参数,保证数据一致性的备份。

mysqldump --single-transaction -uroot -p db_name > test.sql

表级锁:将MySQL的库中某张表锁定,当一个事务获得一个表锁后,其他事务就不能对该表进行任何修改操作,直到该事务释放了对该表的锁定;

# 1、表上锁
lock tables tb_name read/write
# 2、释放锁
unlock tables

记录锁(行级锁):行锁是 MySQL 中最小的锁粒度,指对一行记录进行加锁,当一个事务获得一个行锁后,其他事务就不能对该行进行任何修改操作,直到该事务释放了对该行的锁定,在RC、RR隔离级别下都支持。对于UPDATE、DELETE和INSERT语句,InnoDB会自动给涉及数据集加写锁; 对于普通SELECT语句,InnoDB不会加任何锁。

间隙锁(Gap Lock):锁定索引记录间隙(不含该记录),确保索引记录间隙不变,防止其他事务在这个间隙进行insert,产生幻读。在RR隔离级别下都支持(锁定的是开区间)。

临键锁(Next-Key Lock):行锁和间隙锁组合,同时锁住数据,并锁住数据前面的间隙Gap。在RR隔离级别下支持(锁定的是闭区间)。

几种锁之间的关系如下图:

注:MySQL的行级锁,是由存储引擎来实现的。InnoDB行锁是通过给索引上的索引项加锁来实现的,因此InnoDB这种行锁实现特点:只有通过索引条件检索的数据, InnoDB 才使用行级锁,否则,InnoDB将使用表锁!

2.2、按类型分

共享锁:允许其他事务再加读锁。加锁方式:select…lock in share mode。

排他锁:不允许其他事务再加读锁或者写锁。加锁方式:select…for update

意向锁(表级锁):为了减少加锁操作的存在,在加行级锁时首先给表添加一个意向锁,避免对这张表多行记录更新时进行多次加锁操作。意向锁的主要作用是为了全表更新数据时的提升性能。

SQL语句行锁类型描述
INSERT …   排他锁自动加锁
UPDATE … 排他锁自动加锁
DELETE … 排他锁自动加锁
SELECT--不加任何锁
SELECT … LOCK IN SHARE MODE共享锁需要手动在SELECT之后加LOCK IN SHARE MODE
SELECT … FOR UPDATE 排他锁需要手动在SELECT之后加FOR UPDATE
2.3、加锁规则
2.3.1、主键索引

1)、等值条件:命中索引,加记录锁;未命中,加间隙锁。
2)、范围条件,命中索引,包含where条件的临键区间,加临键锁范围条件;未命中,加间隙锁。

2.3.2、辅助索引

1)、 等值条件:命中索引,命中记录的辅助索引项 + 主键索引项加记录锁,辅助索引项两侧加间隙锁;未命中,加间隙锁
2)、范围条件:命中索引,包含where条件的临键区间加临键锁。命中记录的id索引项加记录锁,未命中,加间隙锁 。

2.4、锁释放

        在事务执行完成之后,提交事务,锁会自动释放。

3、锁相关参数
# 查看行级锁锁相关参数
show status like 'innodb_row_lock%';
# Innodb_row_lock_current_waits: 当前正在等待锁定的数量; 
# Innodb_row_lock_time:从系统启动到现在锁定总时间长度;
# Innodb_row_lock_time_avg:每次等待所花平均时间;
# Innodb_row_lock_time_max:从系统启动到现在等待最常的一次所花的时间; 
# Innodb_row_lock_waits:系统启动后到现在总共等待的次数;
 4、总结

        本文详细介绍MySQL中几种锁类型和使用,帮助大家了解锁的使用,让我们更加深入理解MySQL,在后面的博文中,我将介绍非锁定一致性读相关知识。

        本人是一个从小白自学计算机技术,对运维、后端、各种中间件技术、大数据等有一定的学习心得,想获取自学总结资料(pdf版本)或者希望共同学习,关注微信公众号:it自学社团。后台回复相应技术名称/技术点即可获得。(本人学习宗旨:学会了就要免费分享)