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

Posted

技术标签:

【中文标题】DELETE QUERY 第一次运行缓慢,但第二次(对于相同条件)运行快速 - 如何在第一次运行时使查询快速运行?【英文标题】:DELETE QUERY Runs Slow First Time, but second time onwards (for same condition) runs fast - How to make the query fast at first time running? 【发布时间】:2013-02-21 12:43:26 【问题描述】:

考虑表格:

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 秒)。

但如果我回滚并再次运行 DELETE Query same ORDER_NUM,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

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

【问题讨论】:

可缓存数据?当你重做时 - 它们仍然存在 - 在内存中,另一个问题是索引集群中有许多分裂 - 需要重新组织...... @jasper:insert 200 rows commit 然后运行此命令ANALYZE TABLE &lt;TABLE_NAME&gt; COMPUTE STATISTICS; 然后删除记录。现在检查删除所需的时间 Simon 可能有答案(您的数据现在已缓存),但您需要显示此语句的执行计划。另外,这张桌子上有触发器吗?您在使用 Exadata 吗?您是否有任何on delete cascade FK 引用此表? 请发布解释计划!另外,您要从中删除的表上是否有任何触发器? 第一个查询执行物理读取,第二个执行不执行。这就是问题的答案——数据被缓存了。 【参考方案1】:

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

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

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

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

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

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

【讨论】:

Simon> 如何检查我的 SGA 大小是否正确?有什么我需要查看表级别的吗? EXPLAIN PLAN 结果和 CONSTRAINT 是我首先查看的内容 - 它们很好。我的主要问题是:第一次删除几乎需要 5 秒,而下一次相同的删除(只要条件保持不变)只需要 200 毫秒。我能做些什么来改善这种情况。谢谢 首先,找出你的数据库有多大。您最常访问的表有多大?由于您有当前的统计信息,因此最容易使用 select bytes/1024/1024 mb from dba_segments where owner='SIMON'; 或类似名称。然后,查看v$sga 以检查您的 SGA 有多大。在最好的情况下,“数据库缓冲区”比您的数据大,但这并不总是适用的。如上所述,第二次执行该查询时,所有受影响的块都已经在内存中(数据库缓冲区缓存),因此几乎可以立即删除。这就是第二个查询执行速度如此之快的原因。 谢谢 - 我已经添加了 SET AUTOTRACEON 结果。 @Jasper 这不是问题标题所说的,您是在问为什么。 使用缓冲区缓存建议而不是基于数据库大小的估计可以更好地解决 SGA 的大小问题,我认为 docs.oracle.com/cd/E11882_01/server.112/e16638/…【参考方案2】:

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

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

【讨论】:

好奇被否决而不评论其理由。 这似乎有点过头来解决这个问题,即以相当复杂的方式完全重新设计存储层。 这里的特殊问题是要理解为什么第一次删除比第二次慢,并且它没有直接的解决方案,只是一个解释——这是块是否被缓存的问题(@Simon 的回答)。如果 300 行删除的性能对应用程序来说是个问题,那么解决方案是更高效的缓存,或者更高效的存储以减少对缓存的需求。我将其作为实现此类解决方案的一种可能方式。其他解决方案是可能的——例如调整缓存大小——但集群实现非常有效。【参考方案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 才能进行并行操作。

【讨论】:

【参考方案4】:

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

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

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

(2) 重建索引:alter index "&lt;YourIndex&gt;" rebuild

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

【讨论】:

这不太可能有帮助。表碎片确实不是问题,重建索引只能暂时缓解该问题,直到索引增长到正常大小。对于像 ORDER_NUM 这样的列上的索引,它可能是一个递增的值,索引可能已经非常紧凑。 在我们知道我们是否在谈论相同的概念之前,您将不得不解释“表碎片”的含义,因为在堆表中删除行会产生空间用于在这些块中插入新行,并且空的索引叶块也被重新使用。 @Simon 对这个问题有正确的答案——这个表移动刚刚缓存了两个删除的块。 @Jasper 您的问题与表碎片无关。自动跟踪显示物理读取的问题,当您刚刚移动表时,块已被缓存,因此快速第一次和第二次删除。 如果一个块不能容纳新行,那么移动表不会改变这一点——如果 PCTFREE 设置得太高,那么当然可以减少它。是的,部分清空的叶块不能重复使用,但是您分析了该部分(或未填充的表块)是否是一个重大问题,或者解释了它们如何使第一个表快速运行而第二个表运行缓慢?坦率地说,仅仅建议人们在他们的系统上进行表移动和索引重建是不负责任的,反对它的论点已经非常成熟。 减 9。观众发言了。

以上是关于DELETE QUERY 第一次运行缓慢,但第二次(对于相同条件)运行快速 - 如何在第一次运行时使查询快速运行?的主要内容,如果未能解决你的问题,请参考以下文章

为啥第一次交换尝试有效,但第二次无效? [复制]

每次 TortoiseSVN(通过 *** 连接时)第一次失败,但第二次工作

H5PY键读取缓慢

查询第一次执行慢,但第二次/第三次执行快

第一次在 recyclerview 上显示获取数据,但第二次打开 recyclerview 不显示数据

fread()函数第一次读取成功,但第二次读取失败,为啥为啥为啥,