MySQL笔记(5)行锁表锁全局锁
自存笔记,学习MySQL实战45讲
第五讲:全局锁和表级锁
全局锁
- 一种将整个数据库实例锁定的机制
- 会组织其他事务对数据库的读写访问
- 一般在逻辑备份中使用
实现
在InnoDB中可以使用 mysqldump 的 —single-transaction 选项,结合 InnoDB 的 MVCC 机制实现一致性备份,无需全局锁
1
mysqldump -u root -p --all-databases --single-transaction > backup.sql
在不支持事务的引擎中
1
2
3FLUSH TABLES WITH READ LOCK;
-- 执行备份或其他需要全局一致性的操作
UNLOCK TABLES;
1
2
3
SET GLOBAL READ_ONLY=1;
-- 执行操作
SET GLOBAL READ_ONLY=0;
**建议使用FTWRL**
表级锁
读锁可兼容读锁,写锁阻塞所有锁
表锁
- 锁定粒度为整张表
- 在存储引擎层加锁
- 不仅会对表的写/读加锁,获取到锁的线程也只能对限定的表做限定的写/读操作
1 | LOCK TABLES table_name READ/WRITE |
元数据锁
- 在表结构发生变更时,保护表元数据(结构、权限等)
- 在server层
- MySQL会在执行SQL语句时隐式获取队形的MDL锁,无需手动操作
- 对一个表做增删改查时,加MDL读锁
- 对表结构变更时,加MDL写锁
对热点表DDL变更的挑战
- 热点表:数据量小但是请求频繁
- DDL变更:设计库表结构修改,create/drop等
问题
- MDL锁阻塞:长事务持有MDL读锁,导致DDL所需的MDL写锁无法获取,造成阻塞
- 传统方案局限
- KILL事务:仅能暂时释放锁,但是热点表的新请求会立即持有锁,无法根治
- 强制阻塞DDL:一直等待直到获取锁,导致请求大量超时,影响可用性
解决方案
NOWAIT/WAIT n(MariaDB/AliSQL 支持)
- ALTER TABLE … NOWAIT
- 立即尝试获取 MDL 写锁,若当前无法获取(存在活跃读锁),则直接报错返回,不等待
- ALTER TABLE … WAIT n
- 尝试获取 MDL 写锁,最多等待n秒,若超时仍未获取锁,则报错返回。
第七讲:行锁
行锁
- 锁定表中单行记录
- 需要就加上了,等到事务结束才释放
- 若事务中要锁多个行,把最可能影响并发度的锁往后放
类型
共享锁(Shared Lock, S锁)
- 就是读锁
- 允许并发读
1 | SELECT ... FROM table WHERE id=1 LOCK IN SHARE MODE; -- 显式加S锁 |
排他锁(Exclusive Lock, X锁)
- 与其他任何锁互斥
- INSERT、UPDATE、DELETE自动加X锁
死锁和死锁检测
死锁
- 多事务执行过程中,因争夺互斥资源而形成的循环等待链,导致事务无法继续执行,若无外部干预,则会一直阻塞下去
典型场景
- 事务 A:更新账户 1(ID=1)的余额(加排他锁 X 锁),再尝试更新账户 2(ID=2)的余额(请求 X 锁)
- 事务 B:更新账户 2(ID=2)的余额(加 X 锁),再尝试更新账户 1(ID=1)的余额(请求 X 锁)
结果:
- 事务 A 持有 ID=1 的 X 锁,请求 ID=2 的 X 锁(被事务 B 持有)
- 事务 B 持有 ID=2 的 X 锁,请求 ID=1 的 X 锁(被事务 A 持有)
形成循环等待链,导致死锁。
解决策略
- 直接等待到超时,超市时间可以通过参数innodb_lock_wait_timeout来设置
- 死锁检测,发现死锁后,主动回滚死锁链条中的某一个事务,让其他事务得以继续运行
死锁检测问题
死锁检测会占用大量cpu资源,存在热点行会影响性能问题,cpu利用率高但事务执行少
- 临时关闭死锁检测,可能会出现超时问题
- 在数据库服务端做并发控制,对相同行的更新,在进入引擎之前排队
All articles on this blog are licensed under CC BY-NC-SA 4.0 unless otherwise stated.