【问题标题】:Performance of a sql querysql查询的性能
【发布时间】:2016-05-11 15:59:36
【问题描述】:

我正在大桌子上执行命令。它有大约 7 百万行。

命令是这样的:

select * from mytable;

现在我将行数限制在 3 百万左右。我正在使用这个命令:

select * from mytable where timest > add_months( sysdate, -12*4 ) 

我在时间列上有一个索引。但成本几乎相同。我预计它们会减少。我究竟做错了什么? 有什么线索吗? 先感谢您!

这里解释计划:

【问题讨论】:

  • 能否包含执行计划? EXPLAIN PLAN FOR select * from mytable where timest > add_months( sysdate, -12*4 )
  • 你在看什么告诉你成本是一样的?
  • 您正在从 Oracle 检索大量数据。这可能是获取数据的主要时间。
  • 您将带回 700 万行中的 3 行。让 Oracle 进行索引查找,然后获取数据会比只运行表要慢。当然,读取 300 万行并将它们返回给客户端需要时间。这是通过连接推送的大量数据。想想看——如果每一行只包含 1Kb 的数据,这就像下载一个 3GB 的文件,在 100MBps 的互联网连接下需要 5 分钟,这还不包括读取和丢弃其他 4GB 数据的时间,或假脱机时间显示在屏幕上。

标签: sql oracle performance oracle11g


【解决方案1】:

对 7 个 mio 中的 3 个使用索引。 of rows 很可能会更加昂贵,因此 oracle 对这两个查询进行全表扫描,这是 IMO 正确的。

您可以尝试进行并行 FTS(全表扫描) - 它应该更快,但它会使您的 Oracle 服务器处于更高的负载下,因此不要在负载较重的多用户数据库上执行此操作。 这是一个例子:

select /*+full(t) parallel(t,4)*/ * 
from mytable t
where timest > add_months( sysdate, -12*4 );

【讨论】:

  • 没有其他方法可以让它更快吗?使用 /*full*/ 几乎没有区别。
  • 你的主要目标是什么?您是否将此输出假脱机到文件?你真的需要所有这些数据吗?
  • /*full*/ - 不起作用,它应该是:/*+full(t) parallel(t,4)*/ 否则它将被忽略。检查执行计划 - 如果 CBO(基于成本的优化器)理解提示,您应该会看到 4 个并行工作人员。
  • 哦,好的。 /*+full(t) parallel(t,4)*/ 也被完全忽略...两个命令都得到完全相同的执行计划。
  • 提示应如下所示: [select /*+full(table_alias) parallel(table_alias, parallelism)*/ from table table_alias where ...;] 我想如果你发帖会更容易这里是你的 real 查询,所以我们可以看到为什么 CBO 会忽略你的提示......
【解决方案2】:

要从表中选择非常少的记录,请使用索引。要选择一个重要的部分,请使用分区

在您的情况下,将通过timest 列上的范围分区启用有效访问。

最大的优势是只访问相关的分区。

这里是一个例子

create table test(ts date, s varchar2(4000))
PARTITION BY RANGE (ts)
  (PARTITION t1p1 VALUES LESS THAN (TO_DATE('2010-01-01', 'YYYY-MM-DD')),
   PARTITION t1p2 VALUES LESS THAN (TO_DATE('2015-01-01', 'YYYY-MM-DD')),
   PARTITION t1p4 VALUES LESS THAN (MAXVALUE)
  );

查询

select * from test where ts < to_date('2009-01-01','yyyy-mm-dd');

将仅访问分区 1,即仅在 '2010-01-01' 之前。

在执行计划中查看 pstart 和 dpstop

-----------------------------------------------------------------------------------------------
| Id  | Operation              | Name | Rows  | Bytes | Cost (%CPU)| Time     | Pstart| Pstop |
-----------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT       |      |     5 | 10055 |     9   (0)| 00:00:01 |       |       |
|   1 |  PARTITION RANGE SINGLE|      |     5 | 10055 |     9   (0)| 00:00:01 |     1 |     1 |
|*  2 |   TABLE ACCESS FULL    | TEST |     5 | 10055 |     9   (0)| 00:00:01 |     1 |     1 |
-----------------------------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------

   2 - filter("TS"<TO_DATE(' 2009-01-01 00:00:00', 'syyyy-mm-dd hh24:mi:ss'))

【讨论】:

    【解决方案3】:

    有(至少)两个问题。

    1. add_months( sysdate, -12*4 ) 是一个函数。它不只是一个常数,所以优化器不能在这里使用索引。

    2. 选择 300 万。从 700 万起。无论如何,按索引排列的行不是好主意。是的,你(会)快速通过索引树,但每次你必须去堆(因为你需要 * = 所有行)。这意味着使用这个索引没有任何意义。

    因此索引在这里不起作用。

    【讨论】:

    • 我认为 Oracle 可以为“add_months( sysdate, -12*4 )” 使用索引,因为这个函数没有应用于表的列,所以应该不是问题。但我完全同意你的第二个说法。
    • @MaxU 考虑到sysdate 在查询执行期间可能会发生变化。
    • 我无法使用特定日期对其进行优化,例如“01.01.2015”。
    • @MaMu 那是因为第 2 项:虽然优化器可能使用索引,但它决定完全扫描更好。没错。
    • @Matt, sysdate - 是一个系统函数,将被评估一次,其值将被 CBO 用作常量/文字。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-12-06
    • 2021-11-21
    • 1970-01-01
    相关资源
    最近更新 更多