目录
为什么不使用 set global readonly = true?
当备库用 single-transaction 做逻辑备份的时候,如果从主库的 binlog 传来一个 DDL 语句会怎么样?
全局锁
概念
给整个数据库实例加锁,处于只读状态 ;数据更新语句(增删查),数据定义语句(建表修改表),更新类事务的提交语句均会被阻塞。
命令:Flush tables with read lock(FTWRL)
场景
全库逻辑备份
主库备份:不能执行更新,业务基本停摆。
从库备份:不能执行主库同步来的 binlog,导致主从延迟。
提问
备份为什么需要加锁?
游戏购买业务:①账户余额扣除 -> ②游戏入库
假如此时执行全库备份,并且未加锁,真正执行顺序如下:
不加锁,备份系统得到的库不是一个逻辑时间点,视图逻辑不一致。
为什么不用一致性读?
官方自带逻辑备份工具 mysqldump,使用参数 single-transaction 时,会启动一个事务,确保拿到一致性视图。 MVCC 的支持,此时可以正常更新数据。
像 MyISAM 中引擎不支持事务,备份过程中更新,只能取到最新数据;所以 single-transaction 只适合所有表都使用事务的引擎库。
为什么不使用 set global readonly = true?
- 有些系统,readonly 用来判断是主库还是从库 。
- 客户端发生异常后,整个库会一直保持 readonly 状态,会导致整个库长时间处于不可写状态;而执行 FTWRL,客户端因为异常断开后,MySQL 会自动释放全局锁,恢复正常更新状态。
表级锁
分类
表锁
lock tables ... read/write
会限制其他线程读写,也会限制自己。
如:lock tables t1 read, t2 write;
- 其他线程对 t1 只能读,对 t2 读写操作都会被阻塞。
- 当前线程解锁前,对 t1 只能读,对 t2 只能读写。
元数据锁(meta data lock,MDL)
MySQL5.5 引入
- 读锁:增删查改(读读不互斥)
- 写锁: 表结构变更(读写/写写互斥)
问题
MDL 对小表结构变更的影响?
- MDL锁只有在事务提交时,才释放。
- 只要一处发生阻塞,后续所有加锁操作都会阻塞,表现为完全不可读写。
如何安全变更小表结构?
- kill 掉长事务
- 请求很频繁时,在 alter table 语句中加等待时间,超时则放弃,不阻塞后面业务;后续重试命令。
MDL 读读不互斥,为什么对同一张表进行更新会互斥?
在 MyISAM 上,线程 A B 要对同一张表执行更新操作(都需要 MDL读锁和表的写锁)
- A 加 MDL读锁成功,加表的写锁成功。
- B 加 MDL读锁成功,加表的写锁失败(等待 A 释放)。
- 所以看起来是互斥的。
当备库用 single-transaction 做逻辑备份的时候,如果从主库的 binlog 传来一个 DDL 语句会怎么样?
备份最重要步骤:
- 获取表结构
- 正式导数据(获取 MDL读锁)
- 释放 MDL读锁
不同时刻到达的不同现象:
- 获取表结构前到达:无影响,备份拿到的为 DDL 后的结构。
- 正式导数据前到达:表结构被改过,报错终止。
- 释放 MDL读锁前到达:binlog 被阻塞,主从延迟,等待 MDL读锁释放。
- 最后到达:无影响,备份拿到的为 DDL 前的结构。
行锁
两阶段锁协议
InnoDB 事务中,行锁需要时才加上,事务结束时才释放。
分类
记录锁(Record Lock)
单行记录上锁。
间隙锁(Gap Lock)
范围:左开右开 (0, 5)
隔离级别:可重复读
锁定一个范围内多行(包括不存在的数据),不包括记录本身;让其他事务无法在间隙中插入数据,以此防止同一事务两次读取出现幻读现象(出现上一次不存在的数据)。
特点:不同事务可以加相同的间隙锁,可能会引起死锁(都加锁后,都执行插入)。
临键锁 (Next-Key Lock)
范围:左开右闭 (0, 5],因为加上了行锁
隔离级别:可重复读
记录锁和间隙锁的结合,锁定范围和记录本身;InnoDB 的默认锁。
特点:先加间隙锁,再加行锁
问题
事务会锁多行,对操作进行排序?
最可能造成锁冲突,最可能影响并发度的锁往后放。
购买游戏涉及以下三种操作:
- 修改用户账户余额(update)。
- 修改平台账户余额(update)。
- 用户游戏库添加游戏(insert)。
顺序为 3 1 2
死锁定义/解决策略?
死锁:并发系统中不同线程间存在循环资源依赖,涉及的线程都在等待其他线程释放资源,就会导致这些线程进入无限等待状态。
策略:
- 设置超时时间:超时退出;时间过长,影响并发度;时间过短,容易误判。
- 主动死锁检测:每个事务被锁住时,都要查看当前线程是否被锁住,时间复杂度为线性的 O(n);需要消耗大量 CPU 资源。
如何解决热点行更新导致的性能问题?
临时关闭死锁检测:
- 确保业务不会出现死锁,可能会出现大量的超时(业务有损,死锁检测业务无损) 。
控制并发度:减少 InnoDB 内部死锁检测工作量
- 使用中间件实现或者修改 MySQL 源码:在进入引擎前排队。
- 一行改成逻辑上的多行:平台账户余额记录设成十行(总额为十行之和),每次修改余额随机选择一条记录;资源数量翻倍,冲突概率也成倍降低。



1859

被折叠的 条评论
为什么被折叠?



