Query performance is affected by factors such as architecture design and database table indexing. A poor design can lead to slow SQL statements.
View details about slow SQL queries
View in Slow SQL
Go to the RDS Instances page. In the top navigation bar, select the region where your instance resides. Then, click the ID of the target instance.
In the navigation pane on the left, choose . For more information, see Slow SQL.
The Slow SQL feature supports SQL optimization. You can decide whether to adopt the suggestions based on the diagnostic results, optimization suggestions, and expected benefits.
View in Log Management
Go to the RDS Instances page. In the top navigation bar, select the region where your instance resides. Then, click the ID of the target instance.
In the navigation pane on the left, click Log Management.
On the Log Management page, select Slow Log Details, specify a time range, and then click Query.
The system records SQL statements that run for longer than the value of the log_min_duration_statement parameter. These records are retained for seven days. The system also removes duplicates of similar statements.
NoteThe log_min_duration_statement parameter specifies the threshold for identifying slow SQL statements. The unit is milliseconds (ms). The default value is 1000, which means that SQL statements with a response time that exceeds 1 second (s) are recorded in the slow query log. You can modify this parameter. For more information, see Modify the parameters of an ApsaraDB RDS for PostgreSQL instance.
Analyze slow SQL statements using the EXPLAIN command
After you locate a slow SQL statement, you can use the EXPLAIN command to query its execution plan. This helps you understand the execution cost of the SQL statement.
You can enable the auto_explain.log_analyze, auto_explain.log_buffers, and auto_explain.log_min_duration parameters by setting instance parameters. This lets you view the execution plans of slow SQL statements in the Slow Query Log Details.
EXPLAIN [ ( option [, ...] ) ] statementThe EXPLAIN command supports the following optional parameters:
Parameter | Data type | Description |
ANALYZE | boolean | Executes the SQL statement and returns the actual running time. Default: |
VERBOSE | boolean | Displays extra information about the plan. Default: |
COST | boolean | Displays the startup time, total execution cost, estimated number of rows, and width of each row for every node in the execution plan. Default: |
BUFFERS | boolean | Information about buffer usage. For example, the number of shared block hits, reads, dirties, and writes; local block hits, reads, dirties, and writes; and temporary block reads and writes. Default: |
TIMING | boolean | Displays the actual startup time and the time spent in each plan node. Default: Note This parameter is available only when ANALYZE is set to |
SUMMARY | boolean | Includes summary information after the query plan, such as the total time spent. Default: Note This parameter is available only when ANALYZE is set to |
FORMAT | None | Specifies the output format. Valid values:
|
For example, running EXPLAIN SELECT * FROM test; returns the following result:
QUERY PLAN
----------------------------------------------------
Seq Scan on test (cost=0.00..1.01 rows=1 width=4)
(1 row)In the preceding result, cost=0.00..1.01 rows=1 width=4 is the estimated execution cost of the SQL statement. 0.00 is the startup cost required to return the first row. 1.01 is the total cost to return all rows.
Notes on EXPLAIN (ANALYZE) for DML SQL statements
The EXPLAIN (ANALYZE) statement and the EXPLAIN statement work in a similar way. The main difference is that the EXPLAIN (ANALYZE) statement executes the SQL statement. If the execution plan of an SQL query involves data changes such as DML UPDATE, INSERT, or DELETE operations, you must execute the EXPLAIN ANALYZE statement in the transaction. After the SQL statement is executed, you can roll the transaction back.
Example command:
BEGIN;
EXPLAIN (ANALYZE) <DML(UPDATE/INSERT/DELETE) SQL>;
ROLLBACK;How to read an execution plan
The output of the EXPLAIN command is a tree structure called a query plan tree. Each node in the tree contains the node type, the object on which it acts, and other properties. The node types are divided into the following major categories:
Control Node
Scan Node
Materialization Node
Join Node
Scan nodes use multiple scan methods. This topic describes some of the most common methods in detail:
Seq Scan:
A sequential scan of the full table. This method is generally used for queries on tables that do not have an index. For example, the command
explain(ANALYZE,VERBOSE,BUFFERS) select * from class where st_no=2;returns the following result:QUERY PLAN -------------------------------------------------------------------------------------------------- Seq Scan on public.class (cost=0.00..26.00 rows=1 width=35) (actual time=0.136..0.141 rows=1 loops=1) // Seq Scan on public.class indicates the node type and the object it acts on. This means a full table scan is performed on the class table. // (cost=0.00..26.00 rows=1 width=35) is the cost estimate for this node. 0.00 is the startup cost. 26.00 is the total cost. rows is the estimated number of output rows. width is the average width of the output rows. // (actual time=0.136..0.141 rows=1 loops=1) is the actual execution information for this node. 0.136 is the startup time in ms. 0.141 is the time spent in ms. rows is the actual number of output rows. loops is the number of loops. Output: st_no, name // Represents the columns of the SQL output result set. This is displayed only when the VERBOSE option is on. Filter: (class.st_no = 2) // Indicates the Filter operation in the Seq Scan node. During the full table scan, each record is filtered. The filter condition is class.st_no = 2. Rows Removed by Filter: 1199 // Indicates how many rows were filtered. This is VERBOSE information for the Seq Scan node and is displayed only when the VERBOSE option is on. Buffers: shared hit=11 // Indicates that 11 data blocks were hit in the shared cache. This is BUFFERS information for the Seq Scan node and is displayed only when the BUFFERS option is on. Planning time: 0.066 ms // The time taken to generate the query plan. Execution time: 0.160 ms // The actual SQL execution time, excluding the time to generate the query plan.Index Scan:
An index scan. This method is mainly used when the WHERE condition contains an index column. For example, if an index exists on the
st_nocolumn, the commandEXPLAIN (ANALYZE,VERBOSE,BUFFERS) SELECT * FROM class WHERE st_no=2;returns the following result:QUERY PLAN -------------------------------------------------------------------------------------------------- Index Scan using no_index on public.class (cost=0.28..8.29 rows=1 width=35) (actual time=0.022..0.023 rows=1 loops=1) // An index scan is performed on the public.class table using the no_index index. Output: st_no, name Index Cond: (class.st_no = 2) // The index scan condition is class.st_no = 2. Buffers: shared hit=3 Planning time: 0.119 ms Execution time: 0.060 ms (6 rows)The preceding result shows that after an index is used, the scan speed for the same table under the same conditions is faster. When fewer data blocks need to be scanned, the cost is lower and the speed is faster.
Index Only Scan:
A covering index scan. This method returns only the specified index columns. For example, the command
EXPLAIN (ANALYZE,VERBOSE,BUFFERS) SELECT st_no FROM class WHERE st_no=2;returns the following result:QUERY PLAN -------------------------------------------------------------------------------------------------- Index Only Scan using no_index on public.class (cost=0.28..4.29 rows=1 width=4) (actual time=0.015..0.016 rows=1 loops=1) // An index-only scan is performed on the public.class table using the no_index index. Output: st_no Index Cond: (class.st_no = 2) Heap Fetches: 0 // The number of data blocks that need to be scanned. Buffers: shared hit=3 Planning time: 0.058 ms Execution time: 0.036 ms (7 rows)Bitmap Index Scan:
A bitmap index scan is used to scan a table based on the indexes of the table and returns a bitmap. Both bitmap index scans and index scans are used to scan tables based on indexes. However, a bitmap index scan returns a bitmap instead of a tuple. In the bitmap, each bit represents a scanned data block.
Bitmap Heap Scan:
A Bitmap Heap Scan is generally the parent node of a Bitmap Index Scan. It converts the bitmap that is returned by the Bitmap Index Scan into corresponding tuples. Compared with an Index Scan, a Bitmap Index Scan transforms random reads into reads that follow the physical order of data blocks. This greatly improves scan performance when the data volume is large. For example, the command
EXPLAIN (ANALYZE,VERBOSE,BUFFERS) SELECT * FROM class WHERE st_no=2;returns the following result:QUERY PLAN -------------------------------------------------------------------------------------------------- Bitmap Heap Scan on public.class (cost=4.29..8.30 rows=1 width=35) (actual time=0.025..0.025 rows=1 loops=1) Output: st_no, name // A Bitmap Heap Scan is performed on the public.class table. Recheck Cond: (class.st_no = 2) // The condition for the Recheck operation of the Bitmap Heap Scan is class.st_no = 2. Heap Blocks: exact=1 // The exact number of scanned data blocks is 1. Buffers: shared hit=3 -> Bitmap Index Scan on no_index (cost=0.00..4.29 rows=1 width=0) (actual time=0.019..0.019 rows=1 loops=1) // A bitmap index scan is performed using the no_index index. Index Cond: (class.st_no = 2) // The bitmap index condition is class.st_no = 2. Buffers: shared hit=2 Planning time: 0.088 ms Execution time: 0.063 ms (10 rows)NoteIn most cases, an Index Scan is faster than a Seq Scan.
If the result set is a large portion of the total data, an Index Scan can be slower than a Seq Scan. This is because an Index Scan must first scan the index and then read the table data.
If the result set is small but has many tuples, a Bitmap Index Scan performs better than an Index Scan.
If the index covers the result set, an Index Only Scan is usually the fastest because it only scans the index. However, performance can decrease in some cases, such as when a visibility map has not been generated.
Optimization for the SQL queries
This section provides two examples that use the EXPLAIN command to analyze tables before and after SQL optimization to show the difference in performance.
Optimization for a table without an index
For a table named t1 that does not have an index, you can optimize queries by creating an index. To do this, run the CREATE INDEX ON t1(id); command.
Before optimization: When you run the
EXPLAIN ANALYZE SELECT * FROM t1 WHERE id =1;command on table t1, a full table scan is performed. The total execution time is 302.381 ms.After optimization: After you run the
EXPLAIN ANALYZE SELECT * FROM t1 WHERE id =1;command again, the total execution time is reduced to 0.052 ms. This is an improvement of several thousand times.
Optimization for a table with a non-optimal index
Create a table named t2, insert 1 million random data records, and then create an index on the
idcolumn.CREATE TABLE t2(id int,name int); INSERT INTO t2 SELECT random()*(id/100),random()*(id/100) FROM generate_series(1,1000000) t(id); CREATE INDEX idx_t2_id ON t2(id);Execute the
EXPLAIN ANALYZE SELECT * FROM t2 WHERE id=10 AND name=13;command. The following result is returned.QUERY PLAN ---------------------------------------------------------------------------------------------------------------- Index Scan using t2_id_idx on t2 (cost=0.42..12.12 rows=1 width=8) (actual time=0.098..2.631 rows=88 loops=1) Index Cond: (id = 10) Filter: (name = 13) Rows Removed by Filter: 4461 Planning Time: 0.081 ms Execution Time: 2.655 ms (6 rows)The plan shows that an index scan was performed. However, a filter condition still exists on the
namecolumn. This indicates that there is room for optimization.Create a composite index on the
idandnamecolumns of the t2 table by running theCREATE INDEX on t2(id,name);command.Execute the
EXPLAIN ANALYZE SELECT * FROM t2 WHERE id=10 AND name=13;command. The following result is returned.QUERY PLAN --------------------------------------------------------------------------------------------------------------------------- Index Only Scan using t2_id_name_idx on t2 (cost=0.42..15.48 rows=18 width=8) (actual time=0.028..0.134 rows=88 loops=1) Index Cond: ((id = 10) AND (name = 13)) Heap Fetches: 88 Planning Time: 0.198 ms Execution Time: 0.157 ms (5 rows)The total execution time is reduced to
0.157 ms. The optimization is successful.
References
You can enable the automatic SQL optimization feature to promptly diagnose and optimize slow SQL statements when they occur in your database instance. This helps keep your database system running in an optimal state.