簡體   English   中英

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?

考慮表:

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

您可以在第一時間看到非常高的物理讀數。
我可以做任何事來幫助解決這種情況嗎?

理解您的問題的關鍵是理解語句的執行方式。 DELETE是一種相對昂貴的操作,通常會導致性能問題。 以下是Oracle執行DML語句的方式:

  1. 執行DML的第一步是在數據庫緩沖區高速緩存中找到所需的塊(如果它們已經存在),或者從數據文件中將它們復制到緩沖區高速緩存中(慢)。 除此之外,還將還原段的空塊復制到緩沖區高速緩存中。
  2. 然后,鎖定放在受影響的行和索引上。
  3. 之后,生成重做:生成描述對數據塊和撤消塊所做的所有更改的更改向量。 對於DELETE,要寫入撤消塊的更改向量是整行。
  4. 然后,執行DELETE。 將整行從數據塊復制到撤消塊,並刪除數據塊中的行。 例如,DELETE生成的撤消數據比INSERT多得多,因為整行的內容都被復制(因此其他會話可以讀取原始數據,或者可以回滾刪除)。

您的查詢幾乎肯定會第二次運行得更快, 因為所有相關塊已經在數據庫緩沖區緩存中 當然,可以在數據庫緩沖區高速緩存中保存的塊越多,所需的I / O就越少。 確保您的SGA尺寸合適。

所以對於你的問題,我們必須看看以下幾點:

  • 最重要的是, 您的查詢是否使用您的索引? 索引是VALID嗎? 為DELETE查詢運行EXPLAIN PLAN,並檢查是否使用了ORDER_NUM的索引以及如何訪問數據。
  • 桌子上有任何限制因素嗎? 如果存在帶有“ON DELETE CASCADE”的CONSTRAINTS,則可能有其他表受DELETE影響。

因此,對於您的問題,查看執行計划(EXPLAIN PLAN)可能是您最好的選擇。

除緩沖區緩存問題外,提高性能的一種方法是促進具有相同ORDER_NUM的記錄的物理群集。 只有當您最常通過ORDER_NUM選擇記錄組時,這才有意義,但可以通過在散列簇中創建表(以及任何也包含ORDER_NUM的子表)來實現。

好處是具有ORDER_NUM謂詞的查詢將訪問更少的表段塊,因此即使需要物理i / o,您也需要更少的塊。 此外,通過確保每個緩存塊包含您感興趣的更高比例的行,您將更有效地使用緩沖區緩存,並且您將緩存更少的塊。

“你可以在第一時間看到非常高的物理讀數。在這種情況下,我可以做任何幫助嗎?”

您的查詢必須先找到記錄才能消除它們。 如果記錄在內存中的速度很快,如果它們在磁盤上速度會慢一個數量級 了解更多。 這就是發生的事情。 第一次在物理上讀取行並且速度很慢,但現在這些行位於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才能進行並行操作。

大規模DELETE操作從具有索引和約束的表中刪除數百萬行。 此操作是數據庫密集型且耗時的,主要是因為它強制數據庫生成並保存到磁盤的重要數量(可能是千兆字節)的重做和撤消數據。

您可以執行大量DELETE作為批量INSERT操作:您可以插入要保留的數據,而不是刪除不再需要的數據。 此選項的關鍵是使用直接路徑INSERT以最少的日志記錄有效地執行它。

演示了一篇非常好的文章來解決您的問題。

這里引用一條簡短的話:

問題

如何在Oracle數據庫中完成大規模的DELETE操作,而無需支付大量的性能開銷?

解決方案

將直接路徑(直接加載)INSERT(帶APPEND提示的INSERT)執行大規模DELETE操作到其日志參數設置為NOLOGGING的表中。 此操作將比DELETE快得多,並且記錄最少,但您必須在之后進行備份以建立新的基准。

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

直接路徑INSERT是一種特殊的數據庫操作。 與SQL * Loader一樣,它將數據直接寫入數據庫文件,繞過緩沖區緩存。 它以最少的日志記錄執行此操作,僅記錄數據字典更改。 此方法背后的邏輯是,由於數據文件在發生實例故障時已經是最新的,因此不需要進行日志記錄。

直接路徑INSERT很重要的兩個不同情況是:

  1. 數據庫處於noarchivelog模式。 無法進行介質恢復,也不需要重做數據。
  2. 數據庫處於存檔日志模式。 它默認記錄重做塊以進行介質恢復。 但是,如果您將表顯式設置為NOLOGGING模式,則數據庫不會記錄重做塊。

因此,當數據庫處於noarchivelog模式或處於archivelog模式且表處於NOLOGGING模式時,使用直接路徑INSERT,它只執行最小重做日志記錄 - 以保護數據字典。

這可能是由表/索引碎片引起的,當您通過索引訪問數據時,更可能是索引。

對於表級別,您將需要以下兩個步驟,僅用於索引(2):

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

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

如果您正在執行大量刪除和插入操作,或者導致行遷移的更新,則這可能適用於您。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM