前言
mysql 中的锁不是一个平铺的分类,可以从三个维度理解:
锁的范围:全局锁、表锁、行锁
锁的模式:共享锁、排他锁、意向锁
锁住的对象:记录、间隙、元数据、自增计数器等
下面主要以最常用的 innodb 为例。
一、共享锁和排他锁
1. 共享锁 s lock
也叫读锁。事务获得共享锁后可以读取数据:
select * from users where id = 1 for share;
特点:
s 锁和 s 锁兼容
s 锁和 x 锁冲突
多个事务可以同时获得同一记录的共享锁,但不能同时修改它。
2. 排他锁 x lock
也叫写锁。用于修改或删除数据:
select * from users where id = 1 for update; update users set age = 20 where id = 1; delete from users where id = 1;
特点:
x 锁与 s 锁冲突
x 锁与 x 锁冲突
一个事务持有排他锁时,其他事务不能再获得冲突锁。innodb 的基本行锁模式就是 s 锁和 x 锁。
二、意向锁
意向锁是表级别的标记,表示事务准备在表中的某些记录上加什么锁。
| 意向锁 | 含义 |
|---|---|
is | 准备在某些记录上加共享锁 |
ix | 准备在某些记录上加排他锁 |
例如:
update users set age = 20 where id = 1;
大致会产生:
users 表上加 ix 锁
id=1 对应索引记录上加 x 锁
意向锁不是为了锁住整张表的数据,而是让 mysql 快速判断表级锁是否与已有行锁冲突。
兼容关系可以简化为:
| is | ix | s | x | |
|---|---|---|---|---|
| is | 兼容 | 兼容 | 兼容 | 冲突 |
| ix | 兼容 | 兼容 | 冲突 | 冲突 |
| s | 兼容 | 冲突 | 兼容 | 冲突 |
| x | 冲突 | 冲突 | 冲突 | 冲突 |
三、innodb 的记录锁
innodb 所谓的“行锁”,本质上通常是索引记录锁。
update users set name = 'tom' where id = 10;
如果 id 是主键,就会锁住主键索引中 id=10 的记录。
主键索引:1 5 [10] 20
↑
被锁定
因此索引非常重要。如果没有合适索引,mysql可能需要扫描并锁住大量索引记录,实际效果可能接近锁住整张表。
四、间隙锁 gap lock
间隙锁锁住的是两个索引值之间的空隙,而不是现有记录。
假设索引中有:
10、20、30
间隙包括:
(-∞,10) (10,20) (20,30) (30,+∞)
如果锁住 (10,20),其他事务不能在这个范围插入 11~19,但通常不影响修改已经存在的 10 或 20。
间隙锁主要用于防止范围当前读出现幻行。
五、临键锁 next-key lock
临键锁是:
next-key lock = 记录锁 + 记录前面的间隙锁
例如锁住:
(10,20]
意味着同时锁住:
间隙:(10,20) 记录:20
在 rr 隔离级别下执行范围当前读:
select * from users where age between 20 and 30 for update;
innodb 可能使用 next-key lock 锁住相关索引记录及间隙,阻止其他事务修改已有记录或向查询范围插入新记录。
六、插入意向锁
插入意向锁是一种特殊的间隙锁,表示事务准备向某个间隙插入记录。
索引中存在:
10、20
两个事务分别执行:
insert into users(id) values (12); insert into users(id) values (18);
虽然都向 (10,20) 插入,但插入位置不同,通常可以并发。
如果该间隙已经被其他事务的间隙锁或 next-key lock 锁住,插入意向锁就需要等待。
七、表锁
可以显式锁住整张表:
lock tables users read; lock tables users write; unlock tables;
read:当前会话可以读,不能写,其他会话也可以读。write:当前会话可以读写,其他会话通常不能访问。
innodb 主要使用行级锁,一般不建议通过 lock tables 手动锁住整张 innodb 表,因为会明显降低并发能力。
八、元数据锁 mdl
mdl 的全称是 metadata lock,用来保护表结构等数据库对象。
事务执行:
start transaction; select * from users;
会获得 users 的元数据锁。如果事务一直没有结束,另一个会话执行:
alter table users add column phone varchar(20);
可能一直等待,并显示:
waiting for table metadata lock
mdl 防止一个事务使用表时,另一个会话突然删除或改变表结构。事务中的 mdl 通常要等到 commit 或 rollback 才释放,因此未提交的长事务经常造成 ddl 阻塞。
九、auto-inc 锁
这是 innodb 为 auto_increment 分配自增值使用的特殊机制:
create table orders (
id bigint primary key auto_increment
);多个事务同时插入时,innodb 需要协调自增值的分配。是否使用表级 auto-inc 锁、持有多久,取决于插入类型和 innodb_autoinc_lock_mode。当前模式支持传统、连续和交错三种分配策略。
十、全局锁和备份锁
全局读锁可以通过以下命令获得:
flush tables with read lock;
它会对整个 mysql 实例加全局读锁,通常允许查询但阻止数据修改,常用于某些备份场景:
unlock tables;
此外,lock instance for backup 可以获得实例级备份锁,允许普通 dml 继续执行,但阻止可能破坏备份一致性的部分操作。
总结
到此这篇关于mysql数据库有哪些锁的文章就介绍到这了,更多相关mysql锁详解内容请搜索代码网以前的文章或继续浏览下面的相关文章希望大家以后多多支持代码网!
发表评论