DELETE QUERY第一次运行缓慢,但第二次(相同条件)运行速度很快 - 如何在第一次运行时快速查询?

时间:2013-02-21 12:43:26

标签: sql oracle

考虑表:

Table Name:ORDER
Columns: (ID (PK), ORDER_NUM, ORDER_STATUS, etc...)
Index(ORDER_IDX) exists on (ORDER_NUM, ORDER_STATUS) together.
There are various FKs too, on which Indexes exist as well.
There are about 2 million rows in the table.

考虑SQL查询:

DELETE from ORDER where ORDER_NUM=234234;

对于特定的ORDER_NUM值,DELETE Query第一次运行速度非常慢(几乎5秒钟就删除了200行)。

但是如果我回滚并再次为相同 ORDER_NUM运行DELETE Query,DELETE QUERY现在可以在200毫秒内运行。

因此,对于提供给此查询的任何 ORDER_NUM,查询运行速度非常慢。

我可以做些什么来第一次固定查询?我必须重建索引吗?还是其他什么?

我正在使用Oracle SQL客户端工具(如TOAD / SQL-Developer)对此进行测试 - 看到实际使用它的Web应用程序中的这种缓慢行为。

编辑>>>
SET AUTOTRACE ON的结果

QUERY运行时的第一时间

           3  user calls
           0  physical read total multi block requests
     4915200  physical read total bytes
     4915200  cell physical IO interconnect bytes
           0  commit cleanout failures: block lost
           0  IMU commits
           1  IMU Flushes
           0  IMU contention
           0  IMU bind flushes
           0  IMU mbu flush

查询运行时的第二次

           3  user calls
           0  physical read total multi block requests
           0  physical read total bytes
           0  cell physical IO interconnect bytes
           0  commit cleanout failures: block lost
           0  IMU commits
           1  IMU Flushes
           0  IMU contention
           0  IMU bind flushes
           0  IMU mbu flush

EXPLAIN计划 - 在FIRST和SECOND RUN中完全相同 - 如下所示:

    ID     OPERATION          NAME       ROWS    Bytes    Cost(%CPU)     Time<br>
=======================================================================================
    0      DELETE Statement               49     2891     41   (0)       00:00:01
    1      DELETE             ORDER      
    2      INDEX RANGE SCAN   ORDER_IDX   49     2891     3    (0)       00:00:01

您可以在第一时间看到非常高的物理读数 我可以做任何事来帮助解决这种情况吗?

5 个答案:

答案 0 :(得分:17)

理解问题的关键是理解语句的执行方式。 DELETE是一种相对昂贵的操作,通常会导致性能问题。所以这是Oracle执行DML语句的方式:

  1. 执行DML的第一步是在数据库缓冲区缓存中找到所需的块(如果它们已经存在),或者从数据文件中复制它们到缓冲区缓存中(慢)。除此之外,还会将还原段的空块复制到缓冲区缓存中。
  2. 然后,将锁定在受影响的行和索引上。
  3. 之后,生成重做:生成描述对数据块和撤消块所做的所有更改的更改向量。对于DELETE,要写入撤消块的更改向量是整行。
  4. 然后,执行DELETE。将整行从数据块复制到撤消块,并删除数据块中的行。例如,DELETE生成的撤消数据比INSERT多得多,因为整行的内容都被复制(因此其他会话可以读取原始数据,或者可以回滚删除)。
  5. 您的查询几乎肯定会在第二次时运行得更快,因为所有相关块已经在数据库缓冲区缓存中。当然,可以在数据库缓冲区高速缓存中保存的块越多,所需的I / O就越少。 确保您的SGA尺寸合适。

    因此,对于您的问题,我们必须考虑以下几点:

    • 最重要的是,您的查询是否使用您的索引?索引是否为VALID?运行DELETE查询的EXPLAIN PLAN,检查是否使用了ORDER_NUM的索引以及如何访问数据。
    • 桌子上有 CONSTRAINTS 吗?如果存在带有&#34; ON DELETE CASCADE&#34;的CONSTRAINTS,则可能有其他表受DELETE影响。

    因此,对于您的问题,查看执行计划(EXPLAIN PLAN)可能是您最好的选择。

答案 1 :(得分:5)

除缓冲区缓存问题外,提高性能的一种方法是促进具有相同ORDER_NUM的记录的物理群集。只有当您最常按ORDER_NUM选择记录组时才有意义,但可以通过在散列簇中创建表(以及任何也包含ORDER_NUM的子表)来实现。

带来好处的是具有ORDER_NUM谓词的查询将访问更少的表段块,因此即使需要物理i / o,您也需要更少的块。此外,通过确保每个缓存块包含您感兴趣的更高比例的行,您将更有效地使用缓冲区缓存,并且您将缓存更少的块。

答案 2 :(得分:3)

  

“你可以在第一时间看到非常高的物理读数。在这种情况下,我可以做任何帮助吗?”

您的查询必须先找到记录才能消除它们。如果记录在内存中的速度很快,如果它们在磁盘上的速度数量级更慢。 Find out more.所以这就是发生的事情。第一次在物理上读取行并且速度很慢,但现在这些行都在DB缓冲区缓存中,因此后续读取速度更快。

您可以做些什么来提高第一次阅读的效果?那取决于时间的流逝。尽管有几个人问你没有提供the explain plans。在不知道访问路径的情况下,我们可以为您提供更多具体建议。所以这里有几点指示。

physical read total multi block requests为零,因此所有physical read total bytes表示单个块,即索引读取。这需要很长时间,这表明该指数可能很差。要检查的一件事是索引的聚类因子:如果聚类因子接近块数,则索引范围扫描将非常快。您的删除所使用的索引可能具有较差的聚类因子。这就是您需要解释计划的原因:因此您可以调查索引的质量。请阅读Tom Kyte撰写的这篇文章find out more about index clustering

如果您的查询具有较差的聚类因子,则可以对其进行调整限制。你不能抛光谚语。删除需要检索整行,因此您必须阅读该表。所以也许最简单的选择就是加快表读取:

alter session enable parallel dml;

delete /*+ no_index (orders) 
           parallel */
from orders
where order_num=234234;  

您正在使用11g,因此您无需指定the object in the PARALLEL hint。请注意,您需要Enterprise Edition进行并行操作。

答案 3 :(得分:1)

大量DELETE操作会从具有索引和约束的表中删除数百万行。此操作是数据库密集型且耗时的,主要是因为它强制数据库生成并将大量(可能是千兆字节)的重做和撤消数据保存到磁盘。

您可以执行大量DELETE作为批量INSERT操作:您可以插入要保留的数据,而不是删除不再需要的数据。此选项的关键是使用直接路径INSERT以最少的日志记录有效地执行它。

This 演示了一篇非常好的文章来解决您的问题。

这里引用一条简短说明:

问题

如何在Oracle数据库中完成大规模的DELETE操作,而无需支付大量的性能开销?

解决方案

将大量DELETE操作作为直接路径(直接加载)INSERT(带APPEND提示的INSERT)执行到其日志参数设置为NOLOGGING的表中。此操作的完成速度明显快于DELETE并且记录最少,但您必须在之后进行备份以建立新的基准。

为什么INSERT可能比DELETE(或更新)更快

直接路径INSERT是一种特殊的数据库操作。与SQL * Loader一样,它将数据直接写入数据库文件,绕过缓冲区缓存。它以最少的日志记录执行此操作,仅记录数据字典更改。这种方法背后的逻辑是,因为在发生实例故障时数据文件已经是最新的,所以不需要记录。

直接路径INSERT很重要的两个不同情况是:

  1. 数据库处于noarchivelog模式。媒体恢复不是 可能,你也不需要重做数据。
  2. 数据库处于存档日志模式。它记录重做媒体块 默认恢复。但是,如果您明确设置表 NOLOGGING模式,数据库不会记录重做块。
  3. 因此,当数据库处于noarchivelog模式或处于archivelog模式且表处于NOLOGGING模式时,使用直接路径INSERT,它只执行最小重做日志记录 - 以保护数据字典。

答案 4 :(得分:-14)

这可能是由表/索引碎片引起的,当您通过索引访问数据时,更可能是索引。

对于表级别,您将需要以下两个步骤,仅用于索引(2):

(1)处理表碎片:alter table "ORDER" move

(2)重建索引:alter index "<YourIndex>" rebuild

如果你做了很多删除&amp;插入或导致行迁移的更新,这可能适用于您。