昨天提到了数据库的四种隔离级别,它的实现离不开锁。
mysql的锁有三大类
第一类:全局锁
对整个数据库实例加锁。执行后,整个数据库会进入只读状态
执行效果:
- 所有库的所有表都被锁定
- 只能执行查询(SELECT)
- 阻塞所有更新操作(INSERT、UPDATE、DELETE)、DDL(ALTER、DROP)、事务提交
为什么要用全局锁?场景:全库备份(逻辑备份)
全局锁的代价非常直接:业务停摆。
- 加锁期间,所有写入操作都被阻塞
- 如果是电商网站,用户无法下单、无法支付
- 如果是社交应用,用户无法发帖、无法点赞
于是,现代数据库提供了更优雅的解决方案。
MySQL 的 –single-transaction 参数
mysqldump --single-transaction --all-databases > backup.sql
原理:
- 不启用全局锁
- 而是启动一个可重复读隔离级别的长事务
- 利用 MVCC (多版本并发控制),读取这个事务开始时的数据快照
- 备份过程中,业务更新可以正常进行(写操作创建新版本,备份读老版本)
第二类:表级锁:
表锁
表锁有两种
读锁 (READ):所有人都可以读,但谁都不能写(包括加锁者自己)
写锁 (WRITE):只有加锁者自己能读写,别人啥都不能干
元数据锁
是一种自动的、隐式的锁,用来保护表结构不被修改。
两种类型
| 类型 | 加锁时机 | 作用 |
|---|---|---|
| MDL读锁 (共享) | 执行 SELECT、UPDATE、INSERT 等 DML 语句时 | 防止执行 DML 时别人改表结构 |
| MDL写锁 (排他) | 执行 ALTER、DROP、CREATE 等 DDL 语句时 | 防止改表结构时别人还在读写 |
意向锁
用来快速判断表里有没有行锁
没有时
-- 有人要在 users 表上加表锁 LOCK TABLES users WRITE; -- 数据库需要检查:users 表里有没有行被锁住? -- 必须扫描整张表的所有行,看有没有 X锁 → 太慢了!
有了之后
-- 事务A给某一行加行锁(X锁)时,同时自动给 users 表加一个"意向排他锁"
-- 这个意向锁只是一个"标志",不阻塞任何人
-- 事务B想加表锁时,一看 users 表有意向锁,就知道:"哦,表里有行被锁了,我得等着"
-- 不用扫描全表!
注意,元数据锁和意向锁都是自动加的
行级锁
行锁
最基础的锁,锁住索引上的某一条记录。
特点
- 锁的是索引,不是数据行本身
- 即使表没有索引,InnoDB 也会创建隐藏的聚簇索引
- 所以行锁总是锁在索引上
间隙锁
锁住一个范围,但不锁记录本身。这是 InnoDB 在可重复读级别下解决幻读的武器。
临键锁
行锁 + 间隙锁的组合,锁住”一个区间 + 右端点”
结语
既然mysql在选择了隔离级别之后会自动加锁,那为什么还要学习数据库的锁呢?
自动加锁的策略是”保守的”,了解了锁之后,可以在”安全”和”性能”之间做权衡,但自动加锁只会选择”安全”。
同样的业务,不同的设计。不了解锁:被数据库牵着走,问题:高并发下性能差,容易死锁。了解锁:主动设计
不了解锁,你是在使用数据库;了解锁,你是在驾驭数据库。

No responses yet