外观
从锁的类别上分MySQL都有哪些锁
在 MySQL 中,从锁的类别上可以分为多种类型,主要包括共享锁与排他锁、意向锁、记录锁、间隙锁、临键锁、自增锁、元数据锁等。
共享锁(Shared Lock,S 锁)
- 特点:多个事务可以同时对同一资源加共享锁,即共享锁可以共享。事务持共享锁时,其他事务也能获取该资源的共享锁,但不能获取排他锁。
- 使用场景:常用于读操作,允许并发读取数据而不互相阻塞。例如在可重复读隔离级别下,多个事务可以同时对同一行数据加共享锁进行查询操作。
- 示例:
-- 会话 1
START TRANSACTION;
SELECT * FROM table_name WHERE id = 1 LOCK IN SHARE MODE; -- 加共享锁查询
-- 会话 2
START TRANSACTION;
SELECT * FROM table_name WHERE id = 1 LOCK IN SHARE MODE; -- 也能加共享锁查询
UPDATE table_name SET column = 'value' WHERE id = 1; -- 会被阻塞,因为有共享锁存在
排他锁(X 锁)
- 特点:一个事务对资源加排他锁后,其他事务不能再对该资源加任何类型的锁(包括共享锁和排他锁),直到该排他锁被释放。
- 使用场景:用于写操作,如
INSERT
、UPDATE
、DELETE
语句,保证数据修改的独占性。 - 示例:
-- 会话 1
START TRANSACTION;
SELECT * FROM table_name WHERE id = 1 FOR UPDATE; -- 加排他锁查询
-- 会话 2
START TRANSACTION;
SELECT * FROM table_name WHERE id = 1 LOCK IN SHARE MODE; -- 会被阻塞
UPDATE table_name SET column = 'value' WHERE id = 1; -- 会被阻塞
意向锁(Intention Locks)
- 特点:意向锁是表级别的锁,用于表示事务即将对表中的行加共享锁或排他锁。意向锁分为意向共享锁(IS 锁)和意向排他锁(IX 锁)。意向锁不会阻塞除全表扫描以外的任何请求,主要用于表明事务后续要对表中的行加哪种类型的锁,方便判断表级锁和行级锁之间的兼容性。
- 使用场景:当事务要对表中的行加共享锁时,先对表加意向共享锁;要对表中的行加排他锁时,先对表加意向排他锁。
- 示例:
-- 会话 1
START TRANSACTION;
SELECT * FROM table_name WHERE id = 1 FOR UPDATE; -- 会先对表加意向排他锁,再对行加排他锁
-- 会话 2
LOCK TABLES table_name READ; -- 若会话 1 已加意向排他锁,此操作会被阻塞
记录锁(Record Locks)
- 特点:记录锁是对索引记录加的锁,它会锁定索引中的一条记录。例如,在
WHERE
子句中使用唯一索引精确匹配时,可能会使用记录锁。 - 使用场景:主要用于精确匹配索引记录的情况,保证对特定记录的操作原子性。
- 示例:
-- 会话 1
START TRANSACTION;
SELECT * FROM table_name WHERE id = 1 FOR UPDATE; -- 可能对 id = 1 的索引记录加记录锁
-- 会话 2
UPDATE table_name SET column = 'value' WHERE id = 1; -- 会被阻塞
间隙锁(Gap Locks)
- 特点:间隙锁锁定的是索引记录之间的间隙,而不是记录本身。间隙锁的作用是防止其他事务在该间隙插入新记录,从而避免幻读问题。
- 使用场景:在可重复读隔离级别下,当使用范围查询且查询条件没有命中索引记录时,可能会使用间隙锁。
- 示例:
-- 表中有 id 为 1、3 的记录
-- 会话 1
START TRANSACTION;
SELECT * FROM table_name WHERE id BETWEEN 1 AND 3 FOR UPDATE; -- 可能对 (1, 3) 这个间隙加间隙锁
-- 会话 2
INSERT INTO table_name (id, column) VALUES (2, 'value'); -- 会被阻塞
临键锁(Next-Key Locks)
- 特点:临键锁是记录锁和间隙锁的组合,它既锁定索引记录,又锁定该记录前面的间隙。临键锁可以解决幻读问题,是 MySQL InnoDB 存储引擎在可重复读隔离级别下默认使用的锁。
- 使用场景:常用于范围查询和非唯一索引查询,保证数据的一致性和隔离性。
- 示例:
-- 表中有 id 为 1、3 的记录
-- 会话 1
START TRANSACTION;
SELECT * FROM table_name WHERE id > 1 FOR UPDATE; -- 可能对 id = 3 这条记录及 (1, 3)、(3, +∞) 这些间隙加临键锁
-- 会话 2
INSERT INTO table_name (id, column) VALUES (2, 'value'); -- 会被阻塞
UPDATE table_name SET column = 'new_value' WHERE id = 3; -- 会被阻塞
自增锁(AUTO-INC Locks)
- 特点:自增锁是一种特殊的表级锁,用于保证自增列的原子性。当一个事务向包含自增列的表中插入数据时,会获取自增锁,以确保生成的自增值是唯一的。
- 使用场景:在向包含自增列的表中插入数据时使用。
- 示例:
CREATE TABLE test (
id INT AUTO_INCREMENT PRIMARY KEY,
name VARCHAR(50)
);
-- 会话 1
START TRANSACTION;
INSERT INTO test (name) VALUES ('Alice'); -- 获取自增锁
-- 会话 2
INSERT INTO test (name) VALUES ('Bob'); -- 会被阻塞,直到会话 1 释放自增锁
元数据锁(Metadata Locks,MDL)
- 特点:元数据锁用于保护数据库对象的元数据(如表结构),防止在对表进行操作时表结构被修改。元数据锁分为共享元数据锁(用于读操作)和排他元数据锁(用于写操作)。
- 使用场景:在对表进行
SELECT
、INSERT
、UPDATE
、DELETE
等操作时会自动获取共享元数据锁;在修改表结构(如ALTER TABLE
)时会获取排他元数据锁。 - 示例:
-- 会话 1
START TRANSACTION;
SELECT * FROM table_name; -- 获取共享元数据锁
-- 会话 2
ALTER TABLE table_name ADD COLUMN new_column VARCHAR(50); -- 会被阻塞,因为会话 1 持有共享元数据锁