Skip to content

应急性能优化方案

一、 问题背景

在业务高峰期,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_idSHOW PROCESSLIST 中的 Id 来判断线程是否在事务内。
      • 次选: 处于 Sleep 状态但事务内的空闲连接。
    • 风险:
      • KILL 事务内的连接会导致事务回滚,可能影响业务数据一致性。
      • 客户端在连接被服务端 KILL 后,直到下次请求才会感知到 (Lost connection 错误)。如果应用错误处理不当(如使用已失效句柄重试),会误认为 MySQL 持续不可用。
      • 关键: 执行 KILL 操作前务必与业务开发团队沟通。
  • 应急方案 2:跳过权限验证 (Skip Grant Tables) - 极高风险
    • 方法: 重启 MySQL 并添加 --skip-grant-tables 参数。MySQL 将完全跳过连接和语句执行时的所有权限检查。
    • 风险: 巨大的安全漏洞! 任何能连接到数据库的人都可以执行任意操作。强烈不推荐,尤其对于公网可访问的数据库。
    • 注意: MySQL 8.0+ 在启用此参数时,默认会开启 --skip-networking,限制只能本地连接,但风险依然极高。

2. 应对“慢查询”性能问题

  • 原因:
    1. 索引缺失或设计不佳。
    2. SQL 语句写法问题 (如索引列上用函数、隐式类型转换导致索引失效)。
    3. MySQL 优化器选错索引。
  • 应急方案 1:紧急添加索引 (Online DDL)
    • 方法: MySQL 5.6+ 支持 Online DDL (ALTER TABLE ... ADD INDEX ...)。最快应急方式是在主库直接执行 (有风险)。
    • 较安全应急流程 (主备架构):
      1. 在备库 B 执行 SET sql_log_bin=off;
      2. 在备库 B 执行 ALTER TABLE ... ADD INDEX ...;
      3. 执行主备切换 (B 成为主库)。
      4. 在新备库 A (原主库) 执行 SET sql_log_bin=off;
      5. 在新备库 A 执行 ALTER TABLE ... ADD INDEX ...;
    • 注意: 常规变更应使用 gh-ost 等更安全的工具。
  • 应急方案 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(); 使规则生效。
  • 应急方案 3:强制使用索引 (Query Rewrite Plugin)
    • 方法 (MySQL 5.7+): 利用查询重写功能,为选错索引的 SQL 添加 FORCE INDEX (...) 提示。
  • 预防措施 (比应急更重要):
    • 上线前充分测试:
      1. 测试环境开启慢查询日志 (long_query_time=0)。
      2. 使用模拟线上数据进行回归测试。
      3. 检查慢查询日志,重点关注 Rows_examined 是否符合预期。
    • 使用工具: pt-query-digest 等工具可帮助分析慢查询日志。

3. 应对“QPS 突增”问题

  • 原因: 业务高峰、应用 Bug 或新功能导致某个 SQL 的 QPS 异常飙升,压垮数据库。
  • 理想方案: 业务侧修复 Bug 或临时下线问题功能。
  • 数据库侧应急方案 (按风险递增):
    1. 移除白名单/权限 (若功能隔离): 如果问题功能使用独立的 IP 白名单或数据库账号,临时移除其访问权限。这是较安全的 DB 端止血方法。
    2. 删除用户 (若功能隔离): 如果问题功能使用独立的 DB 用户,删除该用户并 KILL 其现有连接。
    3. 查询重写为 SELECT 1 (极高风险): 使用 Query Rewrite Plugin 将压力最大的 SQL 直接改写为 SELECT 1;
      • 风险:
        • 可能误伤使用相同 SQL 模板的其他正常功能。
        • 返回 SELECT 1 可能破坏依赖该查询结果的后续业务逻辑,导致更大范围的功能失败。
      • 定位: 仅作为最后手段,用于暂时阻止数据库彻底崩溃,需极其谨慎并尽快由业务侧解决根本问题。
  • 依赖: 方案 1 和 2 的有效性依赖于规范的运维体系(如账号分离、白名单管理)。

三、 总结与反思

  • 本文介绍的方案多为应急止血,具有不同程度的风险,尤其是跳过权限验证和强制改写 SQL 为 SELECT 1
  • 沟通至上: 任何有损操作(如 KILL 连接、改写 SQL)前,务必与业务开发团队充分沟通。
  • 预防优于治疗: 加强 SQL 审计、上线前充分测试、建立规范的运维体系(账号分离、白名单)能显著减少需要“饮鸩止渴”的场景。
  • 应用健壮性: 应用代码应能正确处理数据库连接异常(如 Lost connection),实现自动重连和重试逻辑。
  • Server 层为主: 本文方案主要集中在 MySQL Server 层面的调整。