2

考虑表:

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 查询第一次运行非常慢(删除 200 行几乎需要 5 秒)。

但是,如果我回滚并针对相同的 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

解释计划- 在第一次和第二次运行中完全相同 - 如下所示:

    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

您可以在第一次看到非常高的物理读取。
我能做些什么来帮助解决这种情况吗?

4

4 回答 4

17

理解你的问题的关键是理解语句是如何执行的。DELETE 是一个相对昂贵的操作,并且经常导致性能问题。下面是 Oracle 执行 DML 语句的方式:

  1. 执行 DML 的第一步是在数据库缓冲区缓存中找到所需的块(如果它们已经存在)或将它们从数据文件复制到缓冲区缓存中(慢)。除此之外,撤消段的空块也被复制到缓冲区缓存中。
  2. 然后,锁定受影响的行和索引。
  3. 之后,生成重做:生成描述对数据块和撤消块所做的所有更改的更改向量。对于 DELETE,要写入撤消块的更改向量是整行。
  4. 然后,执行删除。整行从数据块复制到撤消块,数据块中的行被删除。例如,DELETE 比 INSERT 生成更多的撤消数据,因为整行的内容都被复制(因此其他会话可以读取原始数据或删除可以回滚)。

您的查询几乎可以肯定第二次运行得更快,因为所有相关块都已在数据库缓冲区缓存中。当然,数据库缓冲区缓存中可以保存的块越多,需要的 I/O 就越少。确保您的 SGA 大小合适。

所以对于你的问题,我们要看看以下几点:

  • 最重要的是,您的查询是否使用您的索引?索引是否有效?为您的 DELETE 查询运行 EXPLAIN PLAN 并检查是否使用了 ORDER_NUM 的索引以及如何访问您的数据。
  • 桌子上有任何限制吗?如果存在带有“ON DELETE CASCADE”的约束,则可能有其他表受您的 DELETE 影响。

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

于 2013-02-25T11:22:41.267 回答
5

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

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

于 2013-03-04T13:55:32.437 回答
3

“你可以在第一次看到非常高的物理读取。我可以为这种情况做点什么吗?”

您的查询必须先找到记录,然后才能删除它们。如果记录在内存中速度很快,如果它们在磁盘上,则速度会慢几个数量级找到更多。 所以这就是正在发生的事情。第一次物理读取行时速度很慢,但现在这些行位于 DB 缓冲区缓存中,因此后续读取速度更快。

你可以做些什么来提高第一次读取的性能?嗯,这取决于时间去哪里。尽管有几个人问你没有提供解释计划。在不知道访问路径的情况下,我们无法为您提供太多具体建议。所以这里有几个指针。

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

如果您的查询具有较差的聚类因子,那么您可以调整多少是有限度的。你不能修饰谚语。删除需要检索整行,因此您必须读取表。所以也许最简单的选择就是加快表格读取速度:

alter session enable parallel dml;

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

您使用的是 11g,因此您不需要在 PARALLEL 提示中指定对象。请注意,您确实需要 Enterprise Edition 才能进行并行操作。

于 2013-03-02T09:07:23.520 回答
-14

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

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

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

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

如果您正在执行大量删除和插入,或者导致行迁移的更新,这可能适用于您。

于 2013-02-25T22:20:44.773 回答