如何对慢速查询进行故障排除并提高其在 Amazon RDS for MySQL 中的性能?
我想对慢速查询进行故障排除并提高其在 Amazon Relational Database Service (Amazon RDS) for MySQL 中的性能。
简短描述
在 Amazon RDS 中,以下问题可能会导致查询性能缓慢:
- 工作负载和资源利用率问题,例如索引不足和缓冲池使用效率低下
- 查询执行计划效率低下
- 资源争用
- 阻塞事务
要解决这些问题,请查看 Amazon CloudWatch 指标、性能详情、数据库洞察及增强监控,以识别性能瓶颈。然后,解决瓶颈问题并优化查询性能。
解决方法
重要事项:性能详情将于 2026 年 6 月 30 日到期。您可以在 2026 年 6 月 30 日之前升级到数据库洞察的高级模式。如果您不进行升级,则使用性能详情的数据库集群将默认采用数据库洞察的标准模式。只有数据库洞察的高级模式才支持执行计划和按需分析。如果您的集群默认采用标准模式,则您可能无法在控制台上使用这些功能。要开启高级模式,请参阅开启适用于 Amazon RDS 的数据库洞察的高级模式和开启适用于 Amazon Aurora 的数据库洞察的高级模式。
**注意:**如果您在运行 AWS 命令行界面 (AWS CLI) 命令时收到错误,请参阅 AWS CLI 错误故障排除。此外,请确保您使用的是最新版本的 AWS CLI。
监控您的资源和数据库性能
要对您的查询性能进行故障排除,请查看 Amazon CloudWatch 指标以确定问题的原因。要确定查询何时会增加特定资源的使用量或降低数据库性能,请使用 CloudWatch 控制台或 AWS CLI 监控以下指标:
- DatabaseConnections
- NetworkReceiveThroughput
- WriteThroughput
- ReadThroughput
- WriteLatency
- ReadLatency
- WriteIOPS
- ReadIOPS
- FreeStorageSpace
- BurstBalance
如果您的数据库性能不佳,请检查 RDS 数据库实例状态,确认是否存在可能影响性能的活动或计划进程。此外,请查看您的 Amazon RDS 事件,确认是否存在可能影响数据库性能的事件。
查看您的工作负载和资源利用率
如果您的查询性能缓慢,请检查工作负载中的其他查询,查看其是否会影响您的查询性能。要识别必须优化的查询,您可以开启适用于 Amazon RDS 或 Amazon Aurora 的数据库洞察的高级模式。
如果您的实例发生重启,则您的数据库实例可能会丢失缓存数据并可能降低查询性能。为防止出现这种冷缓存问题,请配置以下参数以在重启后加速缓冲池预热:
- innodb_buffer_pool_dump_at_shutdown
- innodb_buffer_pool_load_at_startup
- innodb_buffer_pool_dump_pct
为了优化查询性能,最佳做法是监控数据库实例对 InnoDB 缓冲池的利用情况。有关详细信息,请参阅 MySQL 网站上的缓冲池。要监控 InnoDB 缓冲池的状态,请查看性能详情的以下数据库计数器:
- 要了解逻辑读取请求的次数,请查看 Innodb_buffer_pool_read_requests 计数器。
- 要了解 InnoDB 缓冲池无法满足,而必须直接从磁盘中读取的逻辑读取次数,请查看 Innodb_buffer_pool_reads。
- 使用 Innodb_buffer_pool_hit_ratio 获取 InnoDB 缓冲池满足读取请求的百分比。
- 查看 Innodb_buffer_pool_usage 了解包含数据页的 InnoDB 缓冲池的百分比。
要识别运行缓慢的查询,您还可以在参数组中打开 slow_query_log,然后将日志发布到 CloudWatch Logs。
优化查询性能
要优化查询性能,请根据查询执行计划的需求运行以下命令。有关详细信息,请参阅 MySQL 网站上的 EXPLAIN 输出格式。
使用 EXPLAIN 优化查询
要查看有关查询性能的详细信息以及查询可能延迟的原因,请运行 EXPLAIN 命令。有关详细信息,请参阅 MySQL 网站上的使用 EXPLAIN 优化查询。
要确定您的查询是否使用索引,请运行 EXPLAIN 查询。在 EXPLAIN 输出中,检查表名称、使用的键以及查询扫描的行数。有关详细信息,请参阅 MySQL 网站上的 EXPLAIN 语句。查看输出,然后执行以下操作:
- 如果输出未显示使用的键,请在 WHERE 子句中的列上创建索引。
- 如果该表具有所需的索引,请确保表统计数据为最新。有关详细信息,请参阅 MySQL 网站上的 INFORMATION_SCHEMA STATISTICS 表。
使用 ANALYZE TABLE 更新查询统计数据
如果您的表统计数据并非最新,则会导致查询性能下降。要更新您的查询统计数据,请运行 ANALYZE TABLE 命令。有关详细信息,请参阅 MySQL 网站上的 ANALYZE TABLE 语句。
使用 EXPLAIN ANALYZE 查看查询的时间分配
要确定查询执行的哪个部分较慢,请运行 EXPLAIN ANALYZE 查询,以查看 MySQL 在查询上的时间分配。查询完成后,EXPLAIN ANALYZE 查询会打印计划及其测量结果。有关详细信息,请参阅 MySQL 网站上的使用 EXPLAIN ANALYZE 获取信息。您还可以使用 SHOW PROFILE 对速度较慢的查询进行分析,并找出会话花费时间最多的状态。有关详细信息,请参阅 MySQL 网站上的 SHOW PROFILE 语句。
使用 SHOW FULL PROCESSLIST 和增强监控来查看操作
运行 SHOW FULL PROCESSLIST 命令查看在数据库服务器上执行的操作列表。您也可以使用增强监控来查看此列表。有关详细信息,请参阅 MySQL 网站上的 SHOW PROCESSLIST 语句。
检查历史列表长度
InnoDB 事务系统维持着多版本并发控制 (MVCC)。如果您的工作负载需要多个打开或长时间运行的事务,则数据库的历史列表长度会很长。最佳做法是避免在数据库上进行打开或长时间运行的事务。有关详细信息,请参阅 InnoDB 历史列表长度显著增加。
如果您不监控历史列表长度,则性能可能会随着时间的推移而下降。历史列表长度过高还可能导致资源利用率高、SELECT 性能缓慢且不一致以及存储空间增加。
**注意:**长时间运行的事务并不是历史列表长度激增的唯一原因。如果清除线程无法匹配数据库的变化,则历史列表长度仍然很高。在极端情况下,您还可能会遇到数据库中断的情况。
SHOW ENGINE INNODB STATUS 命令显示有关事务处理、等待事件和死锁的信息。有关详细信息,请参阅 MySQL 网站上的 SHOW ENGINE 语句。运行 SHOW ENGINE INNODB STATUS 查询来检查历史列表长度:
SHOW ENGINE INNODB STATUS;
输出示例:
\------------ TRANSACTIONS ------------Trx id counter 26368570695 Purge done for trx's n:o < 26168770192 undo n:o < 0 state: running but idle History list length 1839
要使用性能详情检查历史列表长度,请完成以下步骤:
- 打开 Amazon RDS 控制台。
- 在导航窗格中,选择 Performance Insights(性能详情),然后选择要查看其指标的数据库。
- 选择 Metrics(指标)。
- 在 Metrics dashboard(指标控制面板)页面上,选择 Custom dashboard(自定义控制面板)。
- 选择 Add widget(添加小组件),然后选择 Trx Rseg History Len 指标。
- 选择 Add Widget(添加小组件)。
**注意:**如果数据操作语言 (DML) 写入导致历史记录列表长度增加,请数据库管理员结束写入查询。
解决被阻止的查询
如果您的查询运行时间过长,则可能是其他查询阻止了您的查询。在 MySQL 8.0 中,您可以在 data_lock_waits 表的性能架构中找到锁定等待。有关详细信息,请参阅 MySQL 网站上的使用 InnoDB 事务和锁定信息。运行以下查询以识别阻塞事务:
SELECT r.trx\_id waiting\_trx\_id, r.trx\_mysql\_thread\_id waiting\_thread, r.trx\_query waiting\_query, b.trx\_id blocking\_trx\_id, b.trx\_mysql\_thread\_id blocking\_thread, b.trx\_query blocking\_query FROM performance\_schema.data\_lock\_waits w INNER JOIN information\_schema.innodb\_trx b ON b.trx\_id = w.blocking\_engine\_transaction\_id INNER JOIN information\_schema.innodb\_trx r ON r.trx\_id = w.requesting\_engine\_transaction\_id;

