mysql刪除和更新操作對效能的影響

pythontab發表於2014-04-16

刪除和更新操作對效能的影響

刪除和更新操作的開銷往往比插入高,所以一個好的設計需要減少對資料庫的更新和刪除操作。

3.1更新操作

資料庫的更新操作會帶來一連串的“效應”:更新操作需要記錄日誌(以便錯誤時回滾);更新可變長欄位(如,varchar型別)會帶來資料物理儲存的變化(記錄的移動);更新索引欄位會導致索引重建;更新主鍵會導致資料重組等。這一切不但會造成更新操作本身效率低,而且由於磁片碎片的產生會造成以後查詢效能的降低。為了應對這一情況,有兩種策略:一、減少更新次數,把多個欄位的更新寫到同一個語句裡;二、避免更新。這兩種策略分別適用於不同的情況,下面將舉例說明兩種情況。

3.1.1減少更新次數

在整合庫裡有個程式碼清洗過程,就是透過連線程式碼表給業務資料的自編碼欄位賦值。程式碼清洗其實是透過關聯程式碼表來更新業務資料表的一個過程,需要連線多個程式碼表,更新多個自編碼欄位。完成此更新,有兩種更新語句的寫法:一種是寫成多個SQL語句,每個語句更新一個自編碼欄位;另一種寫法是將所有更新寫在一個語句中。更新銀行程式碼的更新語句如下所示:

updateTBL_INCOME_TMP A

setBANKCODESELF = (

    select SELFCODE

    from

        TBL_BANKINFO B

    where A.BANKCODE = B.BANKCODE )

透過一個更新語句實現多個自編碼欄位更新的語句示意如下:

updateTBL_INCOME_TMP

set 程式碼1自編碼 = 透過關聯程式碼1表得到自編碼,

    程式碼2自編碼 =透過關聯程式碼2表得到自編碼,

    ...,

    程式碼n自編碼 =透過關聯程式碼n表得到自編碼

利用兩千萬的測試資料。兩種方法的測試結果如下表所示。從測試結果看出,一次更新方法效能提高了十倍,大大提高了效能。

處理過程

多次更新方法耗時

一次更新方法耗時

程式碼清洗

0:29:48

0:02:59

3.1.2避免更新

下面舉個通俗的例子,這類情況是經常遇到的。某公司有一套系統員工考勤系統,為了提高查詢統計的效能,在原有系統基礎上建立了一些包含冗餘資訊的表。以員工表為例,它獲得資料的過程如圖12所示。第一步把員工資訊放到新表中,然後連線透過欄位“部門ID”連線更新“部門名稱”。


圖12. 關聯更新

一般,為了節省儲存開支把部門名稱這樣的欄位設計成可變長的。所以在對它進行更新時會造成磁碟資料的重新組織,形成磁碟碎片,影響查詢效能。

為了避免這樣的情況發生,我們可以使用如圖13所示的方法避免更新。這種方法一步完成了冗餘資料表的插入,再插入時連線部門表獲得“部門名稱”,從而避免了更新操作。


圖13. 避免更新

3.2刪除操作

初學者可能認為刪除操作很簡單,可以快速完成。其實這是一個錯誤的理解,刪除過程需要大量掃描磁碟;需要記錄資料庫日誌;而且刪除過程不釋放磁碟空間,浪費磁碟,並且使磁碟上的資料支離破碎,這對後續查詢的效能是一個致命的打擊。通常用兩種方式來應對:一、對經常做刪除操作的表進行重組(reorg);二、避免刪除。

3.2.1 重組

重組(reorg)操作會重新排列表資料的物理順序,併除去碎片資料中的空閒空間。

由於刪除操作不釋放磁碟空間,在執行刪除操作後,表會成為碎片狀,這導致效能嚴重下降,在多次更新操作之後也會出現這種情況。若收集了統計資訊,但看不出有明顯的效能改進,則重組表資料可能會有幫助。重組表資料時,根據指定的索引重新安排資料的物理順序,併除去碎片資料中的空閒空間。這使該資料可以更快速的被存取,從而改進效能。

3.2.2 避免刪除——中間表和正式表模式

在資料需要比較複雜的處理的時候經常會用到中間表和正式表模式。資料在中間表中被處理,然後把滿足條件的資料轉移至正式表,不滿足條件的資料保留在中間表中。圖14示意了資料從中間錶轉移到正式表的過程:在完成資料處理之後,需要把中間表temp1中flag = 1的資料插入到正式表,並刪除中間表temp1中flag = 1的資料。


圖14. 從中間表向正式錶轉移資料

因為flag欄位不是聚簇索引,所以當對中間表temp1進行刪除後,會再磁碟中留下大量碎片,如圖15所示。不但會留下那麼多的磁碟碎片,而且已刪除的資料的空間也不會自動釋放。結果是不但浪費磁碟空間,而且查詢效能會急劇下降。


圖15. 刪除操作後的磁碟碎片

我們們可以使用清空表的命令來避免刪除操作。除了中間表temp1和正式表,新增輔助臨時表temp2。如果temp1中保留的資料flag=0只佔有10%,這一最佳化將顯著提升效能。具體步驟如下:

1.         將temp1中flag=0的資料,插入到temp2

2.         清空表temp1

alter table temp1 ACTIVATE NOT LOGGED INITIALLY WITH EMPTY TABLE ;

3.        將temp2中的資料插入temp1

3.3如何使訪問更高效

本小節的內容很大一部分來自《The Art of SQL》這本書,這本書裡集合了資料庫開發的通用經驗。雖然沒有侷限於具體的DBMS和硬體平臺,但是卻是一本實踐性很強的書。

1.一次連線資料庫,做很多事情。直到處理完,才斷開連線。

2.一個SQL語句包含儘量多的操作。形象地說:幾千個語句,藉助遊標不斷迴圈,很慢。換成幾個語句,處理同樣的資料,還是很慢。換成一個語句,解決問題,最好。

3.接近DBMS核心。儘量使用資料庫自帶的函式。減少自定義函式。因為再聰明的資料庫最佳化器也不認識自定義函式。

4.一個語句不要連線太多的表,建議的上限是5個。

5.將頻繁更新的列集中起來:當更新某一行時,DB2 會記錄進行更改的所有列,因此將頻繁更新的列放到一起可以減少 DB2 的記錄工作。這只是一個有關效能的小建議,因此不應為實現它而進行重大的應用程式或資料庫設計修改。

6.如果想了解,如何書寫SQL語句才能更高效,可參考《The Art of SQL》。書中提出了不少有益的見解。


相關文章