考虑表:
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
您可以在第一时间看到非常高的物理读数 我可以做任何事来帮助解决这种情况吗?
答案 0 :(得分:17)
理解问题的关键是理解语句的执行方式。 DELETE是一种相对昂贵的操作,通常会导致性能问题。所以这是Oracle执行DML语句的方式:
您的查询几乎肯定会在第二次时运行得更快,因为所有相关块已经在数据库缓冲区缓存中。当然,可以在数据库缓冲区高速缓存中保存的块越多,所需的I / O就越少。 确保您的SGA尺寸合适。
因此,对于您的问题,我们必须考虑以下几点:
因此,对于您的问题,查看执行计划(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很重要的两个不同情况是:
因此,当数据库处于noarchivelog模式或处于archivelog模式且表处于NOLOGGING模式时,使用直接路径INSERT,它只执行最小重做日志记录 - 以保护数据字典。
答案 4 :(得分:-14)
这可能是由表/索引碎片引起的,当您通过索引访问数据时,更可能是索引。
对于表级别,您将需要以下两个步骤,仅用于索引(2):
(1)处理表碎片:alter table "ORDER" move
(2)重建索引:alter index "<YourIndex>" rebuild
如果你做了很多删除&amp;插入或导致行迁移的更新,这可能适用于您。