AnalyticDB for PostgreSQL records slow queries automatically. To diagnose performance issues, query two built-in views in the postgres database: qmonitor.instance_slow_queries for instance-level analysis, and qmonitor.host_slow_queries for per-node analysis.
Prerequisites
Before you begin, ensure that:
Your instance runs AnalyticDB for PostgreSQL V7.0 in elastic storage mode with minor engine version V7.0.5.0 or later. To check or update the minor engine version, see View the minor engine version and Update the minor engine version.
The slow query diagnostics feature is enabled. See Enable or disable slow query diagnostics.
Usage notes
Slow query logs retain data for the last 7 days and do not record failed queries.
SQL statements longer than 1,024 bytes are truncated in the log.
All SQL statements that take 1 second or longer, plus all DDL statements, are recorded by default. The threshold is controlled by the
slow_query_min_durationGrand Unified Configuration (GUC) parameter. To ensure system stability, we recommend that you do not modify the default setting of this parameter.
Enable or disable slow query diagnostics
Connect to the postgres database and run the following commands.
To check the current status:
SHOW adbpg_feature_enable_query_monitor;To enable the feature for a specific database:
-- Replace <database_name> with the name of your business database.
ALTER DATABASE <database_name> SET adbpg_feature_enable_query_monitor TO ON;To disable the feature, set the value to OFF.
Query slow query logs
All examples below run against qmonitor.instance_slow_queries (instance level) or qmonitor.host_slow_queries (node level). Connect to the postgres database before running them.
For a full description of all fields in both views, see View fields.
By time range
Query all slow statements executed in the past 30 minutes:
SELECT
query_start AS "Start time",
query_end AS "End time",
query_duration_ms AS "Duration (ms)",
query_id AS "Query ID",
query AS "SQL statement"
FROM qmonitor.instance_slow_queries
WHERE query_start >= now() - interval '30 min';Query all slow statements executed on a specific date (for example, February 26, 2024):
SELECT
query_start AS "Start time",
query_end AS "End time",
query_duration_ms AS "Duration (ms)",
query_id AS "Query ID",
query AS "SQL statement"
FROM qmonitor.instance_slow_queries
WHERE query_start >= '2024-02-26 00:00:00'
AND query_end <= '2024-02-27 00:00:00';By resource consumption (instance level)
query_duration_ms is the sum of four sub-fields:
| Sub-field | What it measures |
|---|---|
optimizer_duration_ms | Time to generate the execution plan |
lock_wait_time_ms | Time waiting for locks |
queue_wait_time_ms | Time waiting for a resource queue |
executor_duration_ms | Time to run the query on the execution engine |
When a query is slow, check which sub-field dominates to identify the bottleneck. For example, a high queue_wait_time_ms indicates resource queue contention rather than an execution problem.
Top 20 by CPU time in the past 30 minutes:
SELECT
(cpu_time_ms / 1000)::text || ' s' AS "CPU time",
query_start AS "Start time",
query_end AS "End time",
query_duration_ms AS "Duration (ms)",
query_id AS "Query ID",
query AS "SQL statement"
FROM qmonitor.instance_slow_queries
WHERE query_start >= now() - interval '30 min'
ORDER BY cpu_time_ms DESC
LIMIT 20;Top 20 by memory usage in the past 30 minutes:
SELECT
pg_size_pretty(mem_bytes) AS "Memory consumed",
query_start AS "Start time",
query_end AS "End time",
query_duration_ms AS "Duration (ms)",
query_id AS "Query ID",
query AS "SQL statement"
FROM qmonitor.instance_slow_queries
WHERE query_start >= now() - interval '30 min'
ORDER BY mem_bytes DESC
LIMIT 20;mem_bytes records the cumulative peak memory usage across nodes. This value approximates actual memory usage and is not exact because memory fluctuates during query execution.Top 10 by CPU time in a specific time window (for example, 00:00–12:00 on February 26, 2024):
SELECT
(cpu_time_ms / 1000)::text || ' s' AS "CPU time",
query_start AS "Start time",
query_end AS "End time",
query_duration_ms AS "Duration (ms)",
query_id AS "Query ID",
query AS "SQL statement"
FROM qmonitor.instance_slow_queries
WHERE query_start >= '2024-02-26 00:00:00'
AND query_end <= '2024-02-26 12:00:00'
ORDER BY cpu_time_ms DESC
LIMIT 10;By resource consumption (node level)
Use qmonitor.host_slow_queries to diagnose slow queries on a specific node.
Top 20 by CPU time on a node in the past 30 minutes:
SELECT
(host_cpu_time_ms / 1000)::text || ' s' AS "CPU time",
query_start AS "Start time",
query_end AS "End time",
query_duration_ms AS "Duration (ms)",
query_id AS "Query ID",
query AS "SQL statement"
FROM qmonitor.host_slow_queries
WHERE hostname = '<node_hostname>'
AND query_start >= now() - interval '30 min'
ORDER BY host_cpu_time_ms DESC
LIMIT 20;Top 20 by memory usage on a node in the past 30 minutes:
SELECT
pg_size_pretty(host_mem_bytes) AS "Memory consumed",
query_start AS "Start time",
query_end AS "End time",
query_duration_ms AS "Duration (ms)",
query_id AS "Query ID",
query AS "SQL statement"
FROM qmonitor.host_slow_queries
WHERE hostname = '<node_hostname>'
AND query_start >= now() - interval '30 min'
ORDER BY host_mem_bytes DESC
LIMIT 20;Replace <node_hostname> with the actual hostname of the node to diagnose.
By user
Count slow queries per user in the past 10 minutes to identify which accounts generate the most slow queries:
SELECT
user_name AS "User",
COUNT(1) AS "Slow query count"
FROM qmonitor.instance_slow_queries
WHERE query_start >= now() - interval '10 min'
GROUP BY user_name
ORDER BY COUNT(1) DESC;By query ID
Retrieve the full details of a specific slow query by its ID:
SELECT * FROM qmonitor.instance_slow_queries
WHERE query_id = '<query_id>';Export slow query logs
Use SELECT statements to export data from qmonitor.instance_slow_queries or qmonitor.host_slow_queries to an internal table, an Object Storage Service (OSS) bucket, or a MaxCompute table. For export methods, see Data Lake Analysis.
In AnalyticDB for PostgreSQL, indexes are created for slow query logs and slow query logs are partitioned on query_start. For better performance, always include query_start in the WHERE clause to avoid full table scans.
For example, to export logs from 13:00 to 16:00 on February 26, 2024:
-- Add this condition to your SELECT statement.
WHERE query_start >= '2024-02-26 13:00:00'
AND query_start <= '2024-02-26 16:00:00'Configure thresholds
Only superusers can modify these configuration items.
slow_query_min_duration
Controls the minimum execution time for a query to be recorded as a slow query. The default is 1s. You can also modify this configuration item to collect slow queries that consume less than 1 second.
If execution time is greater than or equal to this value, the SQL statement, execution time, and related metrics are recorded.
Set to
-1to disable slow query collection entirely.
To change the threshold for a specific database (requires superuser):
-- Collect slow queries that take 5 seconds or longer.
ALTER DATABASE '<database_name>' SET slow_query_min_duration = '5s';To change the threshold for the current session only (any user can run this):
SET slow_query_min_duration = '5s';slow_query_plan_min_duration
Controls the minimum execution time for a slow query's execution plan to be collected. The default is 10s.
If execution time is greater than or equal to this value, the execution plan is collected alongside the query metrics.
For ad-hoc plan inspection, use
EXPLAINinstead—you do not need to collect plans automatically.Set to
-1to disable execution plan collection entirely.
To change the threshold for a specific database (requires superuser):
ALTER DATABASE '<database_name>' SET slow_query_plan_min_duration = '10s';To change the threshold for the current session only:
SET slow_query_plan_min_duration = '10s';View fields
qmonitor.instance_slow_queries (instance level)
| Field | Type | Description |
|---|---|---|
query_id | text | Unique ID of the query. |
session_id | integer | ID of the query session. |
db_name | character varying(128) | Name of the database queried. |
user_name | character varying(128) | Name of the user who initiated the query. |
application_name | character varying(128) | Application that initiated the query. |
client_hostname | character varying(128) | Hostname of the client. |
client_addr | character varying(128) | IP address of the client. |
client_port | character varying(32) | Port of the client. |
rsg_name | character varying(128) | Resource group associated with the table accessed by the query. |
query_start | timestamptz | Start time of the query. |
query_end | timestamptz | End time of the query. |
query_duration_ms | bigint | Total query time in milliseconds. Equal to optimizer_duration_ms + lock_wait_time_ms + queue_wait_time_ms + executor_duration_ms. |
optimizer_duration_ms | bigint | Time to generate the execution plan, in milliseconds. Complex SQL statements take longer. |
lock_wait_time_ms | bigint | Lock wait time, in milliseconds. |
queue_wait_time_ms | bigint | Time waiting for a resource queue, in milliseconds. |
executor_duration_ms | bigint | Time to run the query on the execution engine, in milliseconds. |
query | text | Text of the SQL statement. |
is_plpgsql | boolean | Whether the query is a PL/pgSQL stored procedure. |
query_optimizer | character varying(16) | Optimizer used. Valid values: ORCA, Planner. |
access_tables | text | Name of the table accessed by the query. |
result_rows | bigint | Number of rows returned. For INSERT statements, the number of rows inserted. |
num_segments | integer | Number of compute nodes where the query ran. |
num_slices | integer | Number of slices in the query execution plan. |
cpu_time_ms | numeric | Total CPU time in milliseconds, including the coordinator node and all compute nodes. |
mem_bytes | numeric | Cumulative peak memory usage per node. Approximates actual memory usage; not an exact value. |
spill_bytes | numeric | The cumulative value of the maximum numbers of files that are saved to the disks on compute nodes where the query runs. This value roughly reflects the disk space consumed by the query but is not an exact value. |
qmonitor.host_slow_queries (node level)
| Field | Type | Description |
|---|---|---|
hostname | character varying(128) | Hostname of the node. |
hostrole | text | Role of the node. Valid values: master (coordinator node), segment (compute node). |
query_id | text | Unique ID of the query. |
db_name | character varying(128) | Name of the database queried. |
user_name | character varying(128) | Name of the user who initiated the query. |
query_start | timestamptz | Start time of the query. |
query_end | timestamptz | End time of the query. |
query | text | Text of the SQL statement. |
query_duration_ms | bigint | Query execution time, in milliseconds. |
optimizer_duration_ms | bigint | Time to generate the execution plan, in milliseconds. |
host_cpu_time_ms | numeric | CPU time of the query on this node, in milliseconds. |
host_mem_bytes | numeric | Peak memory usage of the query on this node. |
host_spill_bytes | numeric | Cumulative maximum data spilled to disk on this node. |