MySql中的各种锁
# MySql中的各种锁
51博客文章-一文搞定innodb所有锁 (opens new window)
mysql-官网对应文档-8.0以上 (opens new window)
- https://dev.mysql.com/doc/refman/8.4/en/innodb-locking.html
mysql-官网对应文档-5.7 (opens new window)
- https://dev.mysql.com/doc/refman/5.7/en/innodb-locking.html
你看吧,好多技术文档的根源都是英文,所以赶紧把英文学起来。
# 锁的作用
在 MySQL 中,锁的作用是确保多个事务可以并发执行而不会导致数据不一致或冲突。锁机制是数据库管理系统(DBMS)中用于管理并发控制的重要工具。
通过不同的锁机制,MySQL 能够在多用户环境下高效地管理并发访问,防止数据不一致和冲突。理解这些锁的作用和使用场景对于编写高效且可靠的 SQL 查询和事务管理非常重要。
在读-读操作中,是不会存在数据不一致的,而写-写操作,存在数据不一致的问题,当然,读-写操作也存在数据不一致的问题。
# 锁的类型
mysql中的innodb数据引擎,支持几种锁呢?关于这个问题的答案当然要从官网中取寻找。
This section describes lock types used by
InnoDB
.
- Shared and Exclusive Locks (opens new window)
- Intention Locks (opens new window)
- Record Locks (opens new window)
- Gap Locks (opens new window)
- Next-Key Locks (opens new window)
- Insert Intention Locks (opens new window)
- AUTO-INC Locks (opens new window)
- Predicate Locks for Spatial Indexes (opens new window)
官网把锁分成了8类
。我们把前面的两个行级别的锁(Shared and Exclusive Lokes),和两个表级别的锁(Intention Locks)称为锁的基本模式。
后面三个Record Locks、Gap Locks、Next-Key Locks,我们把它们叫做锁的算法,也就是分别在什么情况下锁定什么范围。
剩下的三个分别是插入意向锁(Insert Intention Locks)、自增锁(AUTO-INC Locks)、索引锁(Predicate Locks for Spatial Indexes)。
所以按照锁的对象来说,mysql的innodb引擎支持两种锁:
- 表锁
- 行锁
# 表锁
表锁是对整个表进行锁定,适用于需要对整个表进行读写操作的情况。表锁可以分为共享锁(读锁)和排他锁(写锁)。
共享锁(读锁):
- 语法:
LOCK TABLES table_name READ;
- 作用:允许多个事务同时读取表,但阻止其他事务写入表。
排他锁(写锁):
- 语法:
LOCK TABLES table_name WRITE;
- 作用:只允许一个事务对表进行写操作,阻止其他事务读取或写入表。
# 意向锁
在 InnoDB 存储引擎中,表锁和行锁是两种不同类型的锁,而意向锁(Intention Locks)是用于协调这两种锁的机制。意向锁是一种表级锁,但它并不阻止其他事务对整个表进行操作,而是用来指示事务稍后将在表中的某些行上加锁。
# 意向锁的类型
InnoDB 支持两种类型的意向锁,同时意向锁是由数据库自己维护的。
意向共享锁(Intention Shared Lock, IS):
- 当一个事务打算在表中的某些行上加共享锁(S-Lock)时,它会先在表上加一个意向共享锁。
意向排他锁(Intention Exclusive Lock, IX):
- 当一个事务打算在表中的某些行上加排他锁(X-Lock)时,它会先在表上加一个意向排他锁。
反过来也就是说,如果一张表上面至少有一个意向共享锁,说明有其他的事务给其中的某些数据行加上了共享锁。 如果一张表上面至少有一个意向排它锁,说明有其他事务给其中的某些数据行加上了排它锁。
为什么要这样进行设计呢?为什么是加行锁之前加呢?加表锁为什么不用?
如果说没有意向锁的话,当我们准备给一张表加上表锁的时候,我们首先要做什么?是不是必须先要去判断有没有其他事务锁定了其中的某些行?如果有的话,肯定不能加上表锁。那么这个时候我们就要去扫描整张表才能确定能不能成功加上一个表锁,如果数据量特别大,比如有上千万的数据的时候,加表锁的效率是不是很低?
但现在我们引入了意向锁了,情况就会变成这样。我们只要判断这张表上面有没有意向锁,如果有,再去依次遍历。如果没有,则可以直接加锁。一定程度上,提高了效率。
那为什么加表锁不用?
加意向锁,是为了节省校验行锁的遍历时间,而表锁是不需要遍历的,所以,表锁不用。所以意向锁也是一种表锁。
以下是意向锁与其他锁的兼容性矩阵:
# 行锁
行锁是对表中的具体行进行锁定,适用于需要对特定行进行读写操作的情况。行锁可以分为共享锁(读锁)和排他锁(写锁)。
SELECT ... LOCK IN SHARE MODE
:
- 这条语句会为查询到的行加上共享锁(S-Lock)。
- 共享锁允许多个事务同时读取同一行数据,但阻止其他事务写入该行。
START TRANSACTION;
SELECT * FROM employees WHERE id = 1 LOCK IN SHARE MODE;
-- 其他事务可以读取 id = 1 的行,但不能修改它
COMMIT;
2
3
4
SELECT ... FOR UPDATE
:
- 这条语句会为查询到的行加上排他锁(X-Lock)。
- 排他锁只允许一个事务对某一行进行写操作,阻止其他事务读取或写入该行。
START TRANSACTION;
SELECT * FROM employees WHERE id = 1 FOR UPDATE;
-- 其他事务不能读取或修改 id = 1 的行
COMMIT;
2
3
4
那么有个问题,一行数据上,可以有多个锁吗?
# 行锁锁的是什么
索引
# 锁的范围有3种
InnoDB 存储引擎中的行锁根据锁的范围可以分为几种不同类型,主要包括记录锁(Record Locks)、间隙锁(Gap Locks)和临键锁(Next-Key Locks)。这些锁类型在 InnoDB 的事务隔离级别中起着关键作用,特别是在可重复读(Repeatable Read)和串行化(Serializable)隔离级别下。
- 记录锁(Record Locks)
- 间隙锁(Gap Locks)
- 临键锁(Next-Key Locks)
MySQL 的 MyISAM 存储引擎确实支持锁机制,但与 InnoDB 存储引擎相比,MyISAM 的锁机制较为简单,主要支持表级锁而不是行级锁。
MyISAM 存储引擎不支持事务处理,但它确实支持锁机制。
# 锁的使用
了解了一定的锁知识后,你还要学会怎么使用它,否则它就只是一堆概念,并不会为你提供任何生产力,你并不会成为一位优秀的软件工程师。
对于MySql的innodb来说,如果你不显式地使用锁语句(如 LOCK TABLES
或 SELECT ... FOR UPDATE
),MySQL 的 InnoDB 存储引擎会根据操作类型自动为你加上适当的行级锁。
请记住,默认情况下加的锁是行级锁。InnoDB 是一个支持事务的存储引擎,它通过行级锁来提供更高的并发性能和数据一致性。
但是,并不是所有情况都会默认加锁。
默认情况下,
SELECT
语句不会加锁。UPDATE
,INSERT
, 和DELETE
语句会自动加排他锁(X-Lock)。可以通过
SELECT ... LOCK IN SHARE MODE
和SELECT ... FOR UPDATE
显式地加共享锁或排他锁。
-- 默认情况下,普通的 SELECT 语句不会加锁
SELECT * FROM employees WHERE id = 1;
-- 那它是否会开启事务呢?
# 手动开启事务
START TRANSACTION;
-- 默认情况下,普通的 SELECT 语句不会加锁
SELECT * FROM employees WHERE id = 1;
COMMIT;
2
3
4
5
6
7
8
9
10
11
使用 InnoDB 存储引擎时,单独的一条
SELECT
语句是否会开启事务取决于当前的autocommit
设置。