mysql锁整理
系列文章目录
文章目录
- 系列文章目录
- 一、表级锁
- 1.1 表共享读
- 1.2 表独占写
- 二、行级锁
- 2.1 共享锁(s锁,读锁)
- 2.2 排他锁(x锁, 写锁)
- 三、意向锁(表级锁,辅助行锁)
- 四、间隙锁
一、表级锁
锁定整张表,加锁快,但是并发低,适合全表扫描场景,主要包括:表共享读锁,表独占写锁,意向共享锁,意向排他锁
1.1 表共享读
多个事务可以同时读表,但是不能修改;修改操作会被阻塞,知道读锁释放
语法:lock tables xx read, unlock tables
-- 事务1:获取表读锁
LOCK TABLES employee READ;
-- 可读取数据
SELECT * FROM employee WHERE user_id = 100001; -- 成功
-- 不可修改数据
UPDATE employee SET dept = '技术部' WHERE user_id = 100001; -- 失败(读锁不允许写)-- 事务2:尝试写操作(会阻塞,直到事务1释放锁)
UPDATE employee SET dept = '产品部' WHERE user_id = 100001; -- 阻塞中...-- 事务1:释放锁
UNLOCK TABLES;
-- 事务2的更新操作立即执行
1.2 表独占写
只有持有写锁的事务可读写表,其他事务的读写操作都会被阻塞
语法:lock tables xxx write
-- 事务1:获取表写锁
LOCK TABLES employee WRITE;
-- 可读写数据
UPDATE employee SET dept = '技术部' WHERE user_id = 100001; -- 成功
SELECT * FROM employee WHERE user_id = 100001; -- 成功-- 事务2:尝试读操作(会阻塞)
SELECT * FROM employee WHERE user_id = 100001; -- 阻塞中...-- 事务1:释放锁
UNLOCK TABLES;
-- 事务2的查询立即执行
二、行级锁
锁定满足条件的行,并发度高,但是加锁开销大,速度慢,适合高频更新少量数据的场景
主要包括:共享锁,排他锁
2.1 共享锁(s锁,读锁)
特性:事务获取某行的s锁后,可读取该行。其他事务可以获取s锁(共享读),但是不能获取x锁,写操作会阻塞
语法:select xxx lock in share mode
-- 事务1:获取行共享锁
SELECT * FROM employee WHERE user_id = 100001 LOCK IN SHARE MODE;-- 事务2:可获取同一行的共享锁(共享读)
SELECT * FROM employee WHERE user_id = 100001 LOCK IN SHARE MODE; -- 成功-- 事务2:尝试更新该行(需要X锁,会阻塞)
UPDATE employee SET dept = '技术部' WHERE user_id = 100001; -- 阻塞中...-- 事务1:提交事务(释放S锁)
COMMIT;
-- 事务2的更新操作立即执行
2.2 排他锁(x锁, 写锁)
事务获取某行的x锁后,可读写该行,其他事务无法获取s锁或者x锁(读写均阻塞)
语法:select xxx for update
-- 事务1:显式获取行排他锁
SELECT * FROM employee WHERE user_id = 100001 FOR UPDATE;-- 事务2:尝试读取该行并加S锁(阻塞)
SELECT * FROM employee WHERE user_id = 100001 LOCK IN SHARE MODE; -- 阻塞中...-- 事务2:尝试更新该行(阻塞)
UPDATE employee SET dept = '技术部' WHERE user_id = 100001; -- 阻塞中...-- 事务1:提交事务(释放X锁)
COMMIT;
-- 事务2的操作立即执行
三、意向锁(表级锁,辅助行锁)
意向锁是表级锁,用于标识某个事务即将对表中的行加s锁或者x锁,避免行锁与表锁的冲突判断
四、间隙锁
间隙锁是innodb在可重复读隔离级别下为解决幻读引入的锁,锁定的是索引区间(而不是具体的行)
幻读:同一事务中,两次查询同一范围的数据,结果行数不同(因其他事务插入了新行)。
间隙锁:锁定索引值之间的间隙,阻止其他事务在该区间插入数据。
-- 表中数据:user_id=100001、100003(无100002)
-- 事务1:在RR隔离级别下查询并加锁,触发间隙锁
SELECT * FROM employee WHERE user_id BETWEEN 100001 AND 100003 FOR UPDATE;
-- 此时锁定的间隙:(100001, 100003),阻止插入user_id=100002的行-- 事务2:尝试插入user_id=100002的行(会阻塞)
INSERT INTO employee (user_id, name) VALUES (100002, '赵六'); -- 阻塞中...-- 事务1:提交事务(释放间隙锁)
COMMIT;
-- 事务2的插入操作执行成功
