Oracle DB 中取数据太慢(查询对比)
Fetching data is too slow in Oracle DB (query comparison)
我遇到了以下问题:
我有一个 table 超过 1,000,000,000 条数据。现在我是运行下面的查询(acc_no是主键):
select acc_no from user where acc_no between 753976276998100 and 78776276998199
上面的查询 运行 在不到一秒的时间内获取了 100,000 条记录
但是如果我在同一查询中再添加一列 ("service_no"),
select acc_no,service_no from user where acc_no between 753976276998100 and 78776276998199
.. 需要一分多钟。这是为什么?为什么第一个查询用时不到一秒,而第二个查询用时超过一分钟?
仅供参考:service_no 是 NUMBER 列
如果您查看这两个查询的执行计划,您会发现第一个查询仅通过索引范围扫描完成:
explain plan for
select acc_no from t42
where acc_no between 753976276998100 and 78776276998199;
select * from table (dbms_xplan.display);
----------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
----------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 10 | 0 (0)| |
|* 1 | FILTER | | | | | |
|* 2 | INDEX RANGE SCAN| SYS_C0090827 | 1 | 10 | 2 (0)| 00:00:01 |
----------------------------------------------------------------------------------
...这可能相当快;但是第二个查询有一个额外的步骤,table 通过索引 rowid 访问:
explain plan for
select acc_no, service_no from t42
where acc_no between 753976276998100 and 78776276998199;
select * from table (dbms_xplan.display);
---------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
---------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 14 | 0 (0)| |
|* 1 | FILTER | | | | | |
| 2 | TABLE ACCESS BY INDEX ROWID| T42 | 1 | 14 | 3 (0)| 00:00:01 |
|* 3 | INDEX RANGE SCAN | SYS_C0090827 | 1 | | 2 (0)| 00:00:01 |
---------------------------------------------------------------------------------------------
当您只查询存在于索引中的列时 - acc_no
在本例中,它位于主键的支持索引中 - 只需要触及索引。无需查看基础 table 数据以获得索引列中已有的值。
当您的 select 列表包含索引中 而非 的列时,也必须检索 table 数据,因为另一列 - service_no
不在索引中。那是另一个磁盘操作访问table段中的数据块。 table 数据也可能分散在比索引更多的块中,这会放大效果,因为您可能必须为每个匹配的行获取不同的块。
基本上它必须做更多的工作才能从磁盘访问更多数据,因此需要更长的时间。
我遇到了以下问题: 我有一个 table 超过 1,000,000,000 条数据。现在我是运行下面的查询(acc_no是主键):
select acc_no from user where acc_no between 753976276998100 and 78776276998199
上面的查询 运行 在不到一秒的时间内获取了 100,000 条记录
但是如果我在同一查询中再添加一列 ("service_no"),
select acc_no,service_no from user where acc_no between 753976276998100 and 78776276998199
.. 需要一分多钟。这是为什么?为什么第一个查询用时不到一秒,而第二个查询用时超过一分钟?
仅供参考:service_no 是 NUMBER 列
如果您查看这两个查询的执行计划,您会发现第一个查询仅通过索引范围扫描完成:
explain plan for
select acc_no from t42
where acc_no between 753976276998100 and 78776276998199;
select * from table (dbms_xplan.display);
----------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
----------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 10 | 0 (0)| |
|* 1 | FILTER | | | | | |
|* 2 | INDEX RANGE SCAN| SYS_C0090827 | 1 | 10 | 2 (0)| 00:00:01 |
----------------------------------------------------------------------------------
...这可能相当快;但是第二个查询有一个额外的步骤,table 通过索引 rowid 访问:
explain plan for
select acc_no, service_no from t42
where acc_no between 753976276998100 and 78776276998199;
select * from table (dbms_xplan.display);
---------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
---------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 14 | 0 (0)| |
|* 1 | FILTER | | | | | |
| 2 | TABLE ACCESS BY INDEX ROWID| T42 | 1 | 14 | 3 (0)| 00:00:01 |
|* 3 | INDEX RANGE SCAN | SYS_C0090827 | 1 | | 2 (0)| 00:00:01 |
---------------------------------------------------------------------------------------------
当您只查询存在于索引中的列时 - acc_no
在本例中,它位于主键的支持索引中 - 只需要触及索引。无需查看基础 table 数据以获得索引列中已有的值。
当您的 select 列表包含索引中 而非 的列时,也必须检索 table 数据,因为另一列 - service_no
不在索引中。那是另一个磁盘操作访问table段中的数据块。 table 数据也可能分散在比索引更多的块中,这会放大效果,因为您可能必须为每个匹配的行获取不同的块。
基本上它必须做更多的工作才能从磁盘访问更多数据,因此需要更长的时间。