我们在数据库中有两个相似的表。除了 table_name 外,这些表具有相同的 DDL,它们也具有相似的索引列,重新收集两个表的统计信息并执行索引重建。表之间的唯一区别是表中的数据(行数、行值、不同值等)。当我们在表上运行我们的选择查询时,它们都会产生不同的解释计划。使用 INDEX RANGE SCAN(1 秒)的速度更快。几天前使用 INDEX FULL SCAN(1-3 分钟内完成)较慢的情况在这张表上不是这种情况。每天在两个表上都发生多次插入、更新和删除。我们可以研究哪些内容来重现该问题?
下面是没有问题的表的解释方案
-----------------------------------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
-----------------------------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 5833 | 4 (0)| 00:00:01 |
|* 1 | COUNT STOPKEY | | | | | |
| 2 | VIEW | | 1 | 5833 | 4 (0)| 00:00:01 |
|* 3 | SORT ORDER BY STOPKEY | | 1 | 1173 | 4 (0)| 00:00:01 |
|* 4 | TABLE ACCESS BY INDEX ROWID BATCHED| SAVA_MESSAGES | 1 | 1173 | 4 (0)| 00:00:01 |
|* 5 | INDEX RANGE SCAN | SAVA_IDX_MESSAGES_PROCESS_ID | 1 | | 3 (0)| 00:00:01 |
解释有问题的表的计划(做索引全扫描)
---------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
---------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 5833 | 23 (0)| 00:00:01 |
|* 1 | COUNT STOPKEY | | | | | |
| 2 | VIEW | | 2 | 11666 | 23 (0)| 00:00:01 |
|* 3 | TABLE ACCESS BY INDEX ROWID| MESSAGES | 108K| 104M| 23 (0)| 00:00:01 |
| 4 | INDEX FULL SCAN | MESSAGES_PK | 124 | | 3 (0)| 00:00:01 |
---------------------------------------------------------------------------------------------
我们无法为您解决,但可以给您一些指导。查看 TABLE ACCESS 行上的 Rows 列。最上面的需要 1 行。底部预计有 108,000 行。这至少是它选择全扫描的原因之一(这得益于比顺序单块读取更高效的“分散”多块读取)。要确定原因,我们需要查看统计数据和谓词。顶部的#4 和#5 旁边有一个星号,底部的#3 旁边也有一个星号。下面应该有针对这些步骤编号的谓词脚注。该谓词信息与来自 dba_tab_statistics 和 dba_tab_col_statistics 的统计数据相结合通常能够确定基数(ROWS 列)估计值的原因。然后是索引——了解每种情况下的索引列列表是什么也很重要。需要的索引是否可见,是否处于可用状态也很关键。如果您正在使用绑定变量,请查看您的 sqlid 的 v$sql_bind_capture 并获取绑定的值,以查看绑定查看如何影响它。确保绑定的数据类型与您希望它使用的索引的前导列的数据类型相匹配。
归根结底,如果您无法从结构上或统计上解决问题,那就是提示的作用。只需提示查询执行您希望它执行的操作并完成它:
SELECT /*+ INDEX(messages,IDX_MESSAGES_PROCESS_ID) */ -- or whatever the index is
FROM messages
WHERE ...
当然,这只有在索引可用、可见,甚至远程可用于您的谓词时才有效,唯一的问题是基数计算错误。
@Paul W - 两个表的索引相同,索引有效且可用。我们也在查询中使用相同的文字值。我将分享更详细的信息。我们已经查看了 dba_tab_statistics 和 dba_tab_col_statistics 但仍无法确定问题所在。我们正在检查表的数据分布和数据偏斜度,但想不出如何进一步重现该问题。
正在使用的查询:
SELECT a.* FROM (SELECT t0.message_type AS MESSAGE_TYPE_1, t0.message AS MESSAGE_2, t0.received_time AS RECEIVED_TIME_3, t0.process_start_time AS PROCESS_START_TIME_4, t0.process_id AS PROCESS_ID_5, t0.process_stop_time AS PROCESS_STOP_TIME_6, t0.error AS ERROR_7, t0.formatted_message AS FORMATTED_MESSAGE_8, t0.barcode AS BARCODE_9, t0.cavity_label AS CAVITY_LABEL_10, t0.order_number AS ORDER_NUMBER_11, t0.id AS ID_12, t0.version AS VERSION_13, t0.is_inactive AS IS_INACTIVE_14 FROM gta.messages t0 WHERE t0.process_id = -1 AND ((t0.is_inactive = 0)) ORDER BY t0.id ASC) a WHERE ROWNUM <= 1;
1 - 过滤器(ROWNUM<=1) 3 - filter("T0"."PROCESS_ID"=(-1) AND "T0"."IS_INACTIVE"=0)`
8 recursive calls
0 db block gets
1857241 consistent gets
975801 physical reads
0 redo size
1469 bytes sent via SQL*Net to client
544 bytes received via SQL*Net from client
2 SQL*Net roundtrips to/from client
0 sorts (memory)
0 sorts (disk)
0 rows processed