索引扫描导致大量物理读取(Oracle)
Index scan results in a lot of physical reads (Oracle)
有两个table有超过2000万条记录。数据库版本为Oracle 11.2.0.3.13标准版
SQL> select count(*) from atsd_tra_trade_print;
COUNT(*)
----------
20000000
SQL> select count(*) from atsd_mob_allocation_pos;
COUNT(*)
----------
20551780
我们 运行 对 table 进行了以下查询。 查询结构相似。查询用于分页。 两个查询的排序列都已编入索引并且不包含任何 NULL 值。下面给出了查询和相应的自动跟踪输出/计划。
索引 INDX_ATTP_6 创建于 ATSD_TRA_TRADE_PRINT(TRANSACT_TIME)
索引 INDX_AMAP_6 创建于 ATSD_MOB_SETTLEMENT_POS(DATE_TIME)
索引基数如下
INDX_ATTP_6 - 5972
INDX_AMAP_6 - 5974
上面两列的数据类型都是varchar2.
在刷新 buffer_cache 之后,两个查询都是 运行 。还收集了架构统计信息。
SELECT * FROM ( SELECT T2.*, ROWNUM AS RN FROM (
SELECT *
FROM ATSDN_TRA_TRADE_PRINT T
WHERE LATEST = 1
ORDER BY TRANSACT_TIME) T2)
WHERE RN > 0 and ROWNUM <= 28;
输出:
28 rows selected.
Elapsed: 00:00:00.16
Execution Plan
----------------------------------------------------------
Plan hash value: 3823692003
------------------------------------------------------------------------- -------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
------------------------------------------------------------------------- -------------------------------
| 0 | SELECT STATEMENT | | 28 | 60060 | 8 (0)| 00:00:01 |
|* 1 | COUNT STOPKEY | | | | | |
|* 2 | VIEW | | 28 | 60060 | 8 (0)| 00:00:01 |
| 3 | COUNT | | | | | |
| 4 | VIEW | | 28 | 59696 | 8 (0)| 00:00:01 |
|* 5 | TABLE ACCESS BY INDEX ROWID| ATSD_TRA_TRADE_PRINT | 7488K| 3234M| 8 (0)| 00:00:01 |
| 6 | INDEX FULL SCAN | INDX_ATTP_6 | 57 | | 3 (0)| 00:00:01 |
------------------------------------------------------------------------- -------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
1 - filter(ROWNUM<=28)
2 - filter("RN">0)
5 - filter("LATEST"=1)
Statistics
----------------------------------------------------------
571 recursive calls
0 db block gets
948 consistent gets
49 physical reads
0 redo size
18586 bytes sent via SQL*Net to client
535 bytes received via SQL*Net from client
3 SQL*Net roundtrips to/from client
29 sorts (memory)
0 sorts (disk)
28 rows processed
以上查询按预期执行得很快。但是另一个 table 上的查询导致执行时间很长。我能看到的唯一观察是有大量的物理读取。 两个查询执行计划看起来也很相似。
SELECT * FROM ( SELECT T2.*, ROWNUM as RN FROM (
SELECT *
FROM ATSD_MOB_ALLOCATION_POS AL
WHERE LATEST = 1
ORDER BY DATE_TIME) T2)
WHERE RN > 0 and ROWNUM <= 28;
输出:
28 rows selected.
Elapsed: 00:01:52.28
Execution Plan
----------------------------------------------------------
Plan hash value: 884170602
------------------------------------------------------------------------- ----------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
------------------------------------------------------------------------- ----------------------------------
| 0 | SELECT STATEMENT | | 28 | 38892 | 7 (0)| 00:00:01 |
|* 1 | COUNT STOPKEY | | | | | |
|* 2 | VIEW | | 28 | 38892 | 7 (0)| 00:00:01 |
| 3 | COUNT | | | | | |
| 4 | VIEW | | 28 | 38528 | 7 (0)| 00:00:01 |
|* 5 | TABLE ACCESS BY INDEX ROWID| ATSD_MOB_ALLOCATION_POS | 14M| 5863M| 7 (0)| 00:00:01 |
| 6 | INDEX FULL SCAN | INDX_AMAP_6 | 39 | | 4 (0)| 00:00:01 |
------------------------------------------------------------------------- ----------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
1 - filter(ROWNUM<=28)
2 - filter("RN">0)
5 - filter("LATEST"=1)
Statistics
----------------------------------------------------------
281 recursive calls
0 db block gets
335395 consistent gets
332763 physical reads
116 redo size
13582 bytes sent via SQL*Net to client
535 bytes received via SQL*Net from client
3 SQL*Net roundtrips to/from client
17 sorts (memory)
0 sorts (disk)
28 rows processed
谁能告诉我为什么会这样。提前致谢...:)
问题出在索引结构上......当它以 asc 方式遍历索引 INDX_AMAP_6 时,它必须读取 500 万条记录才能遇到满足过滤条件的行 "LATEST =1".排序列和文件管理器列上的复合索引解决了该问题。
在 atsd_mob_allocation_pos 上创建索引 AMAP_TEST1(最新,date_time);
有两个table有超过2000万条记录。数据库版本为Oracle 11.2.0.3.13标准版
SQL> select count(*) from atsd_tra_trade_print;
COUNT(*)
----------
20000000
SQL> select count(*) from atsd_mob_allocation_pos;
COUNT(*)
----------
20551780
我们 运行 对 table 进行了以下查询。 查询结构相似。查询用于分页。 两个查询的排序列都已编入索引并且不包含任何 NULL 值。下面给出了查询和相应的自动跟踪输出/计划。
索引 INDX_ATTP_6 创建于 ATSD_TRA_TRADE_PRINT(TRANSACT_TIME)
索引 INDX_AMAP_6 创建于 ATSD_MOB_SETTLEMENT_POS(DATE_TIME)
索引基数如下
INDX_ATTP_6 - 5972
INDX_AMAP_6 - 5974
上面两列的数据类型都是varchar2.
在刷新 buffer_cache 之后,两个查询都是 运行 。还收集了架构统计信息。
SELECT * FROM ( SELECT T2.*, ROWNUM AS RN FROM (
SELECT *
FROM ATSDN_TRA_TRADE_PRINT T
WHERE LATEST = 1
ORDER BY TRANSACT_TIME) T2)
WHERE RN > 0 and ROWNUM <= 28;
输出:
28 rows selected.
Elapsed: 00:00:00.16
Execution Plan
----------------------------------------------------------
Plan hash value: 3823692003
------------------------------------------------------------------------- -------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
------------------------------------------------------------------------- -------------------------------
| 0 | SELECT STATEMENT | | 28 | 60060 | 8 (0)| 00:00:01 |
|* 1 | COUNT STOPKEY | | | | | |
|* 2 | VIEW | | 28 | 60060 | 8 (0)| 00:00:01 |
| 3 | COUNT | | | | | |
| 4 | VIEW | | 28 | 59696 | 8 (0)| 00:00:01 |
|* 5 | TABLE ACCESS BY INDEX ROWID| ATSD_TRA_TRADE_PRINT | 7488K| 3234M| 8 (0)| 00:00:01 |
| 6 | INDEX FULL SCAN | INDX_ATTP_6 | 57 | | 3 (0)| 00:00:01 |
------------------------------------------------------------------------- -------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
1 - filter(ROWNUM<=28)
2 - filter("RN">0)
5 - filter("LATEST"=1)
Statistics
----------------------------------------------------------
571 recursive calls
0 db block gets
948 consistent gets
49 physical reads
0 redo size
18586 bytes sent via SQL*Net to client
535 bytes received via SQL*Net from client
3 SQL*Net roundtrips to/from client
29 sorts (memory)
0 sorts (disk)
28 rows processed
以上查询按预期执行得很快。但是另一个 table 上的查询导致执行时间很长。我能看到的唯一观察是有大量的物理读取。 两个查询执行计划看起来也很相似。
SELECT * FROM ( SELECT T2.*, ROWNUM as RN FROM (
SELECT *
FROM ATSD_MOB_ALLOCATION_POS AL
WHERE LATEST = 1
ORDER BY DATE_TIME) T2)
WHERE RN > 0 and ROWNUM <= 28;
输出:
28 rows selected.
Elapsed: 00:01:52.28
Execution Plan
----------------------------------------------------------
Plan hash value: 884170602
------------------------------------------------------------------------- ----------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
------------------------------------------------------------------------- ----------------------------------
| 0 | SELECT STATEMENT | | 28 | 38892 | 7 (0)| 00:00:01 |
|* 1 | COUNT STOPKEY | | | | | |
|* 2 | VIEW | | 28 | 38892 | 7 (0)| 00:00:01 |
| 3 | COUNT | | | | | |
| 4 | VIEW | | 28 | 38528 | 7 (0)| 00:00:01 |
|* 5 | TABLE ACCESS BY INDEX ROWID| ATSD_MOB_ALLOCATION_POS | 14M| 5863M| 7 (0)| 00:00:01 |
| 6 | INDEX FULL SCAN | INDX_AMAP_6 | 39 | | 4 (0)| 00:00:01 |
------------------------------------------------------------------------- ----------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
1 - filter(ROWNUM<=28)
2 - filter("RN">0)
5 - filter("LATEST"=1)
Statistics
----------------------------------------------------------
281 recursive calls
0 db block gets
335395 consistent gets
332763 physical reads
116 redo size
13582 bytes sent via SQL*Net to client
535 bytes received via SQL*Net from client
3 SQL*Net roundtrips to/from client
17 sorts (memory)
0 sorts (disk)
28 rows processed
谁能告诉我为什么会这样。提前致谢...:)
问题出在索引结构上......当它以 asc 方式遍历索引 INDX_AMAP_6 时,它必须读取 500 万条记录才能遇到满足过滤条件的行 "LATEST =1".排序列和文件管理器列上的复合索引解决了该问题。
在 atsd_mob_allocation_pos 上创建索引 AMAP_TEST1(最新,date_time);