How to Investigate a Slow Query in Aurora MySQL
Published 2026-08-13 · Updated 2026-08-13 · Last Verified 2026-08-13
Answer
When a specific query is slow in Aurora MySQL, isolate the cause in this order: (1) identify the slow SQL using the slow query log (`slow_query_log` and `long_query_time`), (2) check running sessions and wait states with `SHOW FULL PROCESSLIST`, and (3) check the execution plan with `EXPLAIN`. If `performance_schema` is enabled, digest aggregation and Performance Insights let you trace wait events as well, but even when it is disabled, these three steps plus CloudWatch metrics can isolate most causes.
Applicability of This Document
| Product | Aurora MySQL (MySQL-compatible edition) |
|---|---|
| Verified Versions | Aurora MySQL 2.x (MySQL 5.7-compatible) / 3.x (MySQL 8.0-compatible). `EXPLAIN ANALYZE` was added in MySQL 8.0, so it is available only on 3.x |
| Environment | Amazon Aurora (AWS) |
| Required Permissions | Read-only operations require `SELECT` on the target schema. Viewing other users' sessions or transactions requires the `PROCESS` privilege. Changing parameters requires IAM `rds:ModifyDBParameterGroup` and master-user-equivalent privileges |
| Execution Impact | No impact for read-only operations. `EXPLAIN ANALYZE` actually executes the target query. Parameter changes constitute a configuration change |
| Restart | Not required for read-only operations. Enabling `performance_schema` requires an instance restart |
| Last Verified | 2026-08-13 |
Ready-to-Run Commands
- 対象
- Aurora MySQL 2.x / 3.x
- 権限
- Connection privilege (read global variables)
- 変更作業
- None (read-only)
- Production実行
- Possible
-- 対象: Aurora MySQL 2.x / 3.x
-- 権限: 接続権限(グローバル変数の参照のみ)
-- 変更作業: なし(参照のみ)
-- Production 実行: 可能
SHOW GLOBAL VARIABLES WHERE Variable_name IN (
'slow_query_log',
'long_query_time',
'log_output',
'log_queries_not_using_indexes',
'min_examined_row_limit',
'performance_schema'
);If `performance_schema` is `OFF`, the digest aggregation described in this article cannot be used. In that case, isolate the cause using the slow query log, PROCESSLIST, `information_schema.INNODB_TRX`, and CloudWatch metrics.
- 対象
- Aurora MySQL 2.x / 3.x
- 権限
- Viewing sessions other than your own requires the `PROCESS` privilege
- 変更作業
- None (read-only)
- Production実行
- Possible
-- 対象: Aurora MySQL 2.x / 3.x
-- 権限: 自分以外のセッションを見るには PROCESS 権限
-- 変更作業: なし(参照のみ)
-- Production 実行: 可能
SELECT
ID,
USER,
HOST,
DB,
COMMAND,
TIME AS elapsed_sec,
STATE,
LEFT(INFO, 200) AS query_head
FROM information_schema.PROCESSLIST
WHERE COMMAND <> 'Sleep'
ORDER BY TIME DESC;This provides the same information as `SHOW FULL PROCESSLIST` in a form that can be sorted and filtered. On MySQL 8.0-compatible Aurora MySQL 3.x, `performance_schema.processlist` is also available; since its implementation doesn't take a global mutex, it has less impact in environments with many connections.
- 対象
- Aurora MySQL 2.x / 3.x
- 権限
- `PROCESS` privilege
- 変更作業
- None (read-only)
- Production実行
- Possible
-- 対象: Aurora MySQL 2.x / 3.x
-- 権限: PROCESS 権限
-- 変更作業: なし(参照のみ)
-- Production 実行: 可能
SELECT
trx_id,
trx_state,
trx_started,
TIMESTAMPDIFF(SECOND, trx_started, NOW()) AS open_sec,
trx_mysql_thread_id,
trx_rows_locked,
trx_rows_modified,
LEFT(trx_query, 200) AS current_query
FROM information_schema.INNODB_TRX
ORDER BY trx_started ASC;If the slowness is caused by lock waiting rather than a specific query, you'll see transactions here that have remained `RUNNING` for a long time. This can be queried regardless of whether `performance_schema` is enabled.
- 対象
- Aurora MySQL 2.x / 3.x (assumes `performance_schema = ON`)
- 権限
- `SELECT` on `performance_schema`
- 変更作業
- None (read-only)
- Production実行
- Possible
-- 対象: Aurora MySQL 2.x / 3.x(performance_schema = ON が前提)
-- 権限: performance_schema への SELECT
-- 変更作業: なし(参照のみ)
-- Production 実行: 可能
SELECT
SCHEMA_NAME,
LEFT(DIGEST_TEXT, 200) AS digest_head,
COUNT_STAR AS exec_count,
ROUND(SUM_TIMER_WAIT / 1000000000000, 3) AS total_sec,
ROUND(AVG_TIMER_WAIT / 1000000000000, 6) AS avg_sec,
SUM_ROWS_EXAMINED AS rows_examined,
SUM_ROWS_SENT AS rows_sent,
SUM_NO_INDEX_USED AS no_index_used,
SUM_CREATED_TMP_DISK_TABLES AS tmp_disk_tables,
FIRST_SEEN,
LAST_SEEN
FROM performance_schema.events_statements_summary_by_digest
ORDER BY SUM_TIMER_WAIT DESC
LIMIT 20;The unit of `*_TIMER_WAIT` is picoseconds, so it is divided by 10^12 to convert to seconds. The aggregation is cumulative since instance startup and is reset on restart or failover.
- 対象
- `EXPLAIN` works on 2.x / 3.x; `EXPLAIN ANALYZE` only on 3.x (MySQL 8.0-compatible)
- 権限
- `SELECT` on the target table
- 変更作業
- None. However, `EXPLAIN ANALYZE` actually executes the target query
- Production実行
- `EXPLAIN` is possible. `EXPLAIN ANALYZE` only when the execution load is acceptable
-- 対象: EXPLAIN は Aurora MySQL 2.x / 3.x、EXPLAIN ANALYZE は 3.x(MySQL 8.0互換)のみ
-- 権限: 対象テーブルへの SELECT
-- 変更作業: なし(EXPLAIN ANALYZE はクエリを実際に実行する点に注意)
-- Production 実行: EXPLAIN は可能/EXPLAIN ANALYZE は実行負荷を許容できる場合のみ
-- 1) 実行計画だけを見る(クエリは実行されない)
EXPLAIN
SELECT id, name, updated_at
FROM SampleDB.sample_table
WHERE status = 'active'
AND updated_at >= '2026-08-01'
ORDER BY updated_at DESC
LIMIT 100;
-- 2) 実測値つきの実行計画(Aurora MySQL 3.x のみ。クエリを実際に実行する)
EXPLAIN ANALYZE
SELECT id, name, updated_at
FROM SampleDB.sample_table
WHERE status = 'active'
AND updated_at >= '2026-08-01'
ORDER BY updated_at DESC
LIMIT 100;`EXPLAIN ANALYZE` returns actual measured row counts and timing instead of estimates, so you can directly see the gap between estimate and reality. However, since it actually executes the query, do not use it for write queries or heavy aggregations. `EXPLAIN FORMAT=JSON` returns details including cost values without actually executing the query.
- 対象
- Aurora MySQL 2.x / 3.x (DB parameter group and DB cluster settings)
- 権限
- IAM: `rds:ModifyDBParameterGroup`, `rds:ModifyDBCluster`
- 変更作業
- Yes (parameter change and log output setting change)
- Production実行
- Carry out only after going through change management. Expect an increase in log volume
# 対象: Aurora MySQL 2.x / 3.x(DBパラメータグループとDBクラスター設定)
# 権限: IAM rds:ModifyDBParameterGroup, rds:ModifyDBCluster
# 変更作業: あり(パラメータ変更 + ログ出力先の変更)
# Production 実行: 変更管理を通したうえで実施。ログ量とストレージ増加を見込むこと
# 1) スロークエリログを有効化する(いずれも動的パラメータとして扱われる想定。
# 実際の反映方法は describe-db-parameters の ApplyType で確認すること)
aws rds modify-db-parameter-group \
--db-parameter-group-name example-aurora-mysql-params \
--parameters '[
{"ParameterName":"slow_query_log","ParameterValue":"1","ApplyMethod":"immediate"},
{"ParameterName":"long_query_time","ParameterValue":"1","ApplyMethod":"immediate"},
{"ParameterName":"log_output","ParameterValue":"FILE","ApplyMethod":"immediate"}
]'
# 2) クラスター単位でスロークエリログをCloudWatch Logsへ出力する
aws rds modify-db-cluster \
--db-cluster-identifier example-aurora-cluster \
--cloudwatch-logs-export-configuration '{"EnableLogTypes":["slowquery","error"]}' \
--apply-immediately
# 3) 反映後の値を確認する
aws rds describe-db-parameters \
--db-parameter-group-name example-aurora-mysql-params \
--query "Parameters[?ParameterName=='slow_query_log' || ParameterName=='long_query_time'].{Name:ParameterName,Value:ParameterValue,Apply:ApplyType,Status:ApplyMethod}" \
--output tableThe smaller `long_query_time` is set, the more queries get logged, increasing log volume and CloudWatch Logs cost. Start at around 1 second and lower it as needed. Setting `log_output` to `TABLE` lets you query the `mysql.slow_log` table with SQL, but since writes become table inserts, the nature of the load changes.
- 対象
- Aurora MySQL 2.x / 3.x (DB parameter group)
- 権限
- IAM: `rds:ModifyDBParameterGroup`, `rds:RebootDBInstance`
- 変更作業
- Yes (static parameter change + instance restart)
- Production実行
- Not possible due to the restart involved. Plan execution during a maintenance window
# 対象: Aurora MySQL 2.x / 3.x(DBパラメータグループ)
# 権限: IAM rds:ModifyDBParameterGroup, rds:RebootDBInstance
# 変更作業: あり(静的パラメータ変更 + インスタンス再起動)
# Production 実行: 不可(再起動を伴うためメンテナンス時間帯に計画実行)
# 1) performance_schema を有効化する(再起動時に反映)
aws rds modify-db-parameter-group \
--db-parameter-group-name example-aurora-mysql-params \
--parameters '[
{"ParameterName":"performance_schema","ParameterValue":"1","ApplyMethod":"pending-reboot"}
]'
# 2) 反映のためインスタンスを再起動する(接続断が発生する)
aws rds reboot-db-instance --db-instance-identifier example-aurora-instance`performance_schema` is a parameter that requires a restart. Because the restart causes a connection drop with an impact equivalent to a failover, always treat it as a planned outage. Some detailed information in Performance Insights also depends on `performance_schema`. The increase in memory usage from enabling it varies by environment, so measure it in a test environment beforehand.
How to Read the Results
| Column | Meaning | What to Check |
|---|---|---|
| SCHEMA_NAME | Schema the digest belongs to | Narrow down to the target application's schema |
| digest_head | Beginning of the SQL statement with literals normalized | SQL statements of the same shape are grouped together. Refer to `DIGEST_TEXT` directly for the full text |
| exec_count | Number of executions (`COUNT_STAR`) | Distinguish whether a single execution is slow, or the total is large due to a high execution count |
| total_sec | Cumulative execution time (seconds) | The top entries are the main cause of load. Check this first |
| avg_sec | Average execution time per call (seconds) | A query with a small total but large average can become a problem depending on when it runs |
| rows_examined | Cumulative number of rows scanned | If this is orders of magnitude larger than `rows_sent`, the index is likely not being used effectively |
| rows_sent | Cumulative number of rows returned | The ratio to `rows_examined` is a measure of efficiency |
| no_index_used | Number of executions that did not use an index | If nonzero, check the access method with `EXPLAIN` |
| tmp_disk_tables | Number of times a temporary table was created on disk | If large, it's a candidate for reviewing sorting/grouping logic |
| FIRST_SEEN / LAST_SEEN | Time first/last observed | Check whether this matches when slowness began |
When to Use This
- The same query that was fine until yesterday suddenly became slow at some point
- Timeouts have increased on the application side, but CPU usage is not high
- Only a specific screen or batch job is slow; other processing is normal
- Tried to check Performance Insights, but details don't appear because `performance_schema` is disabled
- The same query remains in `SHOW PROCESSLIST` for a long time
Possible Causes (Most Likely First)
01
The execution plan changed and the index is no longer being used
An increase in data volume or a statistics update can cause the optimizer to choose a different access method. If `rows_examined` is extremely large compared to `rows_sent`, this is likely the cause.
02
Execution is stalled due to lock waiting
Even if the query itself is lightweight, it will wait if another transaction holds a row lock. Check whether `information_schema.INNODB_TRX` shows a transaction that has remained open for a long time.
03
Scan cost increased due to growing data volume
Even with the same execution plan, processing time increases as the number of target rows grows. In this case, the index design or search conditions need to be reviewed; parameter tuning alone will not resolve it.
04
Sort operations or temporary tables are spilling to disk
When the target of `ORDER BY` or `GROUP BY` is large, a temporary table is created on disk. This can be checked via `SUM_CREATED_TMP_DISK_TABLES` in the digest aggregation.
05
Replica lag or resource contention on the reader instance side
If reads are directed to the reader endpoint, the load situation differs between the writer and reader. First isolate which instance is slow.
Diagnostic Steps
- 1
Identify which instance and time window
Read-onlyCheck CloudWatch's `CPUUtilization`, `DatabaseConnections`, `ReadLatency` / `WriteLatency`, and `Deadlocks` to narrow down the time window and instance where the event occurred.
- 2
Check the slow query log settings
Read-onlyRun the `SHOW GLOBAL VARIABLES` command above to check whether logging is actually happening and whether `long_query_time` matches the real situation.
- 3
Check running sessions
Read-onlyUse `information_schema.PROCESSLIST` to check whether the slow query is actually running or waiting. The `STATE` column is a clue.
- 4
Check long-running transactions and locks
Read-onlyRefer to `information_schema.INNODB_TRX`. This can be run even when `performance_schema` is disabled.
- 5
Identify top offenders via digest aggregation
Read-onlyIf `performance_schema` is enabled, check `events_statements_summary_by_digest` sorted by cumulative execution time in descending order.
- 6
Check the execution plan of the target SQL
LowUse `EXPLAIN` to check the access method, index used, and estimated row count. Only use `EXPLAIN ANALYZE` (Aurora MySQL 3.x) when you want to see the gap between estimate and actual, and only within a load the system can tolerate.
Solutions
Low-risk action that can be done immediately
Enable the slow query log to pin down the target SQL
HighBefore acting on guesswork, confirm the actually slow SQL via the log. Start `long_query_time` at around 1 second.
Terminate the lock-holding session from the application side
HighIf a long-open transaction is the cause, first stop the processing on the application side. `KILL` on the DB side is a last resort, and it rolls back the in-progress update.
Change requiring advance review
Add an index matching the search conditions
HighBased on the `EXPLAIN` result, consider a composite index that satisfies both filtering and sorting. Since adding an index is a schema change, it requires verification in a test environment and scheduling for an appropriate time window.
Rewrite the query to reduce the number of rows scanned
MediumReduce `rows_examined` through changes such as not applying functions to condition columns, returning only necessary columns, and reviewing the paging approach.
Export the slow query log to CloudWatch Logs for ongoing monitoring
HighInstead of viewing logs only temporarily, aggregate them so trends can be tracked over time. Costs accrue in proportion to log volume.
Change involving a restart or service impact
Enable performance_schema
HighThis is a static parameter that requires a restart. Since it causes a connection drop, treat it as a planned outage. After enabling it, digest aggregation and detailed Performance Insights information become available.
Change the instance class
HighThis is an option when CPU or memory is chronically insufficient, but it does not solve execution-plan problems for a single query. Finish isolating the cause first before deciding.
!Warnings
- `EXPLAIN ANALYZE` actually executes the target query. Do not use it for write queries or heavy aggregations.
- Enabling `performance_schema` involves an instance restart. Since it causes a connection drop, it cannot be done without downtime.
- Setting `long_query_time` extremely low makes the log output itself a source of load and storage consumption.
- `KILL` triggers a rollback of the in-progress transaction, and the rollback can take as long as or longer than the original processing. Do not use it lightly.
- The digest aggregation is a cumulative value since instance startup. Immediately after a failover or restart, the sample size is too small to be a useful basis for judgment.
- This article describes a procedure for isolating the cause; it does not guarantee that applying any specific setting will make things faster.
Differences by Version and Environment
If This Doesn't Resolve It
Check application-side timeout and connection pool settings
If things look slow even though the DB-side execution time hasn't changed, connection waiting or pool exhaustion can be the cause.
Isolate whether the slowness is on the writer or reader side
Run the same SQL on the writer and reader separately to see if there's a difference. If there is, suspect an instance-specific load factor.
Check CloudWatch's `ReadIOPS` / `WriteIOPS` and buffer pool hit ratio
The approach differs depending on whether the wait is on the storage side or the CPU side.
Cross-reference schema changes or deployment history with the timing
Check whether an index was dropped or an application query was changed around the time things slowed down.
Check whether the same symptom appears across multiple queries
The direction of investigation differs depending on whether it's a single-query problem or an instance-wide problem.
Basis and Limitations of This Document
Explanation based on official product documentation
This is a general verification procedure based on MySQL's slow-query-log-related system variables, the public specifications of `information_schema.PROCESSLIST` / `INNODB_TRX`, `performance_schema.events_statements_summary_by_digest`, and `EXPLAIN` / `EXPLAIN ANALYZE`, as well as the public specifications of Amazon RDS / Aurora parameter groups and log output. It does not include measured values from any specific environment.
FAQ
Can this be run in production?
`SHOW GLOBAL VARIABLES`, `information_schema.PROCESSLIST`, `information_schema.INNODB_TRX`, the digest aggregation, and `EXPLAIN` are all read-only and can be run as-is in production. `EXPLAIN ANALYZE` actually executes the query, and parameter changes constitute a configuration change, so check the impact of each before carrying them out.
Can the cause be investigated even if performance_schema is disabled?
Yes. You can identify the slow SQL via the slow query log, check the running state and locks with `SHOW FULL PROCESSLIST` and `information_schema.INNODB_TRX`, and view the execution plan with `EXPLAIN` — this flow can isolate most causes. Since enabling it requires a restart, first exhaust what you can do while it stays disabled.
Is a restart required to enable performance_schema?
Yes. Change `performance_schema` to 1 in the DB parameter group and restart the target instance to apply it. Since the restart causes a connection drop, treat it as a planned outage.
What permissions are required?
If you only need `EXPLAIN` on your own session and the target table, `SELECT` on the target schema is enough. Viewing other users' sessions or `INNODB_TRX` requires the `PROCESS` privilege. Parameter changes require IAM privileges (such as `rds:ModifyDBParameterGroup`).
How should the results be interpreted?
If `rows_examined` is orders of magnitude larger than `rows_sent`, it's an access-method problem; if `INNODB_TRX` shows a long-running transaction, it's lock waiting; if neither applies and CPU or I/O is pegged, it's a resource issue. Sort the digest aggregation by "cumulative execution time" and distinguish single-call slowness from high call count.
Is it okay to use EXPLAIN ANALYZE in production?
Use it only after confirming the target is read-only and the load is acceptable to run. In exchange for returning actual measurements instead of estimates, `EXPLAIN ANALYZE` runs the query to completion. If you're unsure, use `EXPLAIN FORMAT=JSON` instead.
Questions This Document Covers
- Want to enable and check the slow query log in Aurora MySQL
- How to proceed with performance analysis in an environment where performance_schema=OFF
- Want to check running queries and wait states in MySQL
- Want to judge from EXPLAIN results whether the index is not being used effectively
What the Risk Labels Mean
- Read-onlyDoes not change data or settings.
- LowImpact is limited, but permissions and load should be checked.
- MediumMay affect performance, locking, or cost.
- HighMay cause an outage, data loss, or require recovery work.
- Expert Review RequiredRequires a separate review before applying to production.
GIIP's Scope of Support
The procedure in this article can get you as far as identifying a single slow query. What's difficult in actual operations is that information from "the moment" it became slow often isn't preserved. GIIP continuously collects the slow query log and CloudWatch metrics, and also records the running sessions and transaction state at the moment a threshold is exceeded, so that an investigation can begin afterward without waiting to reproduce the issue. Day-to-day collection and initial triage are handled by an AI agent, and a human takes over at the stage where judgment is required, such as changing an execution plan or adding an index.
Author & Technical Review
GIIP Production Operations Team
About 30 years of experience in designing, migrating, and operating large-scale web services, SQL Server, Oracle, AWS, and Azure. Experience includes 12 sets of x12large-class AWS RDS for SQL Server environments, an Oracle environment with roughly 120,000 tables, and migrating roughly 3TB from TiDB to Aurora MySQL. AI agents and human experts currently continue to monitor and operate multiple cloud databases and about 30 web services.
How to Collect and Review Audit Logs in Aurora MySQL
A procedure for enabling Aurora MySQL's Advanced Auditing via a cluster parameter and reviewing it through CloudWatch Logs or RDS log files. Also organizes how to narrow down what gets recorded.
aurora-mysqlWhat to Check When Migrating from utf8mb3 to utf8mb4 in Aurora MySQL
The first thing that breaks when converting to utf8mb4 is index key length. This organizes the SQL for finding targets, how to choose a collation, the impact of conversion, and where the settings live.
sql-serverSQL to Check Statistics Last-Updated Time per Table in SQL Server
A read-only SQL script using sys.stats and STATS_DATE that lists the last-updated time and the number of rows modified since the last update, per table and statistic.
monitoringWhat to Configure When Monitoring Servers and Databases 24/7
A checklist, organized by layer, of what to monitor, how to think about thresholds, the escalation structure, and why synthetic monitoring is necessary when designing 24/7 monitoring.
Related Services
Request an investigation into the slow query's cause
If you need to repeat the same checks across multiple environments, we can discuss your entire operations setup.
Request an investigation into the slow query's cause