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



