应急性能优化方案
一、 问题背景
在业务高峰期,MySQL 可能因压力过大而无法正常响应。此时,可能需要采取一些临时性、有风险的手段来快速恢复服务,这些方法如同“饮鸩止渴”,旨在短期内缓解症状,但可能带来副作用或长期隐患。常规的无损优化方案应在平时实施。
二、 “饮鸩止渴”方案详解
1. 应对“短连接风暴” (连接数暴增)
- 问题: MySQL 建立连接成本高(网络握手、权限验证)。短连接模式下,当数据库处理变慢时,大量新连接涌入,可能迅速耗尽
max_connections
上限,导致服务拒绝新连接("Too many connections")。 - 错误尝试: 直接大幅调高
max_connections
。- 风险: 可能让更多连接涌入,加剧系统负载(尤其在权限验证等环节),导致已有连接也无法获得足够资源执行 SQL,适得其反。
- 应急方案 1:清理空闲连接 (Kill Idle Connections)
- 方法: 使用
KILL CONNECTION <thread_id>;
主动断开连接。wait_timeout
参数会自动断开超时空闲连接,但可能不够及时。 - 目标选择:
- 优先: 处于
Sleep
状态且事务外的空闲连接。可通过查询information_schema.innodb_trx
表,对比trx_mysql_thread_id
和SHOW PROCESSLIST
中的Id
来判断线程是否在事务内。 - 次选: 处于
Sleep
状态但事务内的空闲连接。
- 优先: 处于
- 风险:
KILL
事务内的连接会导致事务回滚,可能影响业务数据一致性。- 客户端在连接被服务端
KILL
后,直到下次请求才会感知到 (Lost connection
错误)。如果应用错误处理不当(如使用已失效句柄重试),会误认为 MySQL 持续不可用。 - 关键: 执行
KILL
操作前务必与业务开发团队沟通。
- 方法: 使用
- 应急方案 2:跳过权限验证 (Skip Grant Tables) - 极高风险
- 方法: 重启 MySQL 并添加
--skip-grant-tables
参数。MySQL 将完全跳过连接和语句执行时的所有权限检查。 - 风险: 巨大的安全漏洞! 任何能连接到数据库的人都可以执行任意操作。强烈不推荐,尤其对于公网可访问的数据库。
- 注意: MySQL 8.0+ 在启用此参数时,默认会开启
--skip-networking
,限制只能本地连接,但风险依然极高。
- 方法: 重启 MySQL 并添加
2. 应对“慢查询”性能问题
- 原因:
- 索引缺失或设计不佳。
- SQL 语句写法问题 (如索引列上用函数、隐式类型转换导致索引失效)。
- MySQL 优化器选错索引。
- 应急方案 1:紧急添加索引 (Online DDL)
- 方法: MySQL 5.6+ 支持 Online DDL (
ALTER TABLE ... ADD INDEX ...
)。最快应急方式是在主库直接执行 (有风险)。 - 较安全应急流程 (主备架构):
- 在备库 B 执行
SET sql_log_bin=off;
- 在备库 B 执行
ALTER TABLE ... ADD INDEX ...;
- 执行主备切换 (B 成为主库)。
- 在新备库 A (原主库) 执行
SET sql_log_bin=off;
- 在新备库 A 执行
ALTER TABLE ... ADD INDEX ...;
- 在备库 B 执行
- 注意: 常规变更应使用
gh-ost
等更安全的工具。
- 方法: MySQL 5.6+ 支持 Online DDL (
- 应急方案 2:改写 SQL (Query Rewrite Plugin)
- 方法 (MySQL 5.7+): 利用
query_rewrite.rewrite_rules
表,将有问题的 SQL 模式 (pattern
) 改写 (replacement
) 为优化后的版本。- 例如:
WHERE id + 1 = ?
改写为WHERE id = ? - 1
。 - 执行
CALL query_rewrite.flush_rewrite_rules();
使规则生效。
- 例如:
- 方法 (MySQL 5.7+): 利用
- 应急方案 3:强制使用索引 (Query Rewrite Plugin)
- 方法 (MySQL 5.7+): 利用查询重写功能,为选错索引的 SQL 添加
FORCE INDEX (...)
提示。
- 方法 (MySQL 5.7+): 利用查询重写功能,为选错索引的 SQL 添加
- 预防措施 (比应急更重要):
- 上线前充分测试:
- 测试环境开启慢查询日志 (
long_query_time=0
)。 - 使用模拟线上数据进行回归测试。
- 检查慢查询日志,重点关注
Rows_examined
是否符合预期。
- 测试环境开启慢查询日志 (
- 使用工具:
pt-query-digest
等工具可帮助分析慢查询日志。
- 上线前充分测试:
3. 应对“QPS 突增”问题
- 原因: 业务高峰、应用 Bug 或新功能导致某个 SQL 的 QPS 异常飙升,压垮数据库。
- 理想方案: 业务侧修复 Bug 或临时下线问题功能。
- 数据库侧应急方案 (按风险递增):
- 移除白名单/权限 (若功能隔离): 如果问题功能使用独立的 IP 白名单或数据库账号,临时移除其访问权限。这是较安全的 DB 端止血方法。
- 删除用户 (若功能隔离): 如果问题功能使用独立的 DB 用户,删除该用户并
KILL
其现有连接。 - 查询重写为
SELECT 1
(极高风险): 使用 Query Rewrite Plugin 将压力最大的 SQL 直接改写为SELECT 1;
。- 风险:
- 可能误伤使用相同 SQL 模板的其他正常功能。
- 返回
SELECT 1
可能破坏依赖该查询结果的后续业务逻辑,导致更大范围的功能失败。
- 定位: 仅作为最后手段,用于暂时阻止数据库彻底崩溃,需极其谨慎并尽快由业务侧解决根本问题。
- 风险:
- 依赖: 方案 1 和 2 的有效性依赖于规范的运维体系(如账号分离、白名单管理)。
三、 总结与反思
- 本文介绍的方案多为应急止血,具有不同程度的风险,尤其是跳过权限验证和强制改写 SQL 为
SELECT 1
。 - 沟通至上: 任何有损操作(如
KILL
连接、改写 SQL)前,务必与业务开发团队充分沟通。 - 预防优于治疗: 加强 SQL 审计、上线前充分测试、建立规范的运维体系(账号分离、白名单)能显著减少需要“饮鸩止渴”的场景。
- 应用健壮性: 应用代码应能正确处理数据库连接异常(如
Lost connection
),实现自动重连和重试逻辑。 - Server 层为主: 本文方案主要集中在 MySQL Server 层面的调整。