在實際工作中,經常會遇到這樣的需求:定期對已經過期的數據進行刪除,釋放服務器資源。例如,我們定義了一個定時器任務,在凌晨2點開始執行,這個任務就是刪除1個月之前的數據,以釋放空間。
最初的時候,我們可以這么寫:
delete from table_name where t_time < to_date(‘2015-04-01’,’yyyy-mm-dd’)
這個一個最簡單不過的根據條件進行刪除數據的操作。乍看之下沒有任何問題,但是當數據量十分龐大的時候,一個月就有上千萬條數據的時候,就會大量占用數據庫的資源。凌晨2點執行的語句,可能要到中午12點才能執行完畢,以至於這段時間內,查詢操作就會變得無比緩慢,平時半分鍾返回結果的查詢,此時就會變成了十幾分鍾。這顯然是不可取的。
在delete操作中,sql語句首先要掃描表或者索引找到符合條件的記錄,然后把它們刪除。這個過程會消耗大量的CPU資源和產生大量的IO,同時還會產生大量的undo數據。一次性刪除10000條左右的數據,差不多要產生了10000多個數據塊讀取,將近3MB的redo日志。
與delete相比,drop和truncate操作消耗的資源要相對小的多。同樣是刪除10000條數據,delete會產生10000多次的數據塊讀取;dro會產生1100多次的數據塊讀取;truncate則只有600多次的數據塊讀取。
一般情況下,我們刪除表中數據是為了釋放空間。delete操作不能釋放空間,刪除了哪個表中的數據,騰出的空間也只能提供給哪個表使用,並不能讓給其他的對象使用。如果希望通過刪除數據騰出空間給其他對象使用,delete是不行的。
drop和truncate操作騰出的空間則能夠提供其他對象使用。
也許有人會問,drop和truncate操作消耗的系統資源和速度是比delete快,但是truncate會清空表中所有的數據,drop更是連表都一起刪除了,這樣顯然不能實現博客開始提出的定時刪除表中上個月數據的功能。這種情況下我們就要用到表分區了。
根據時間字段t_time按照每個月一個分區的方式來創建一個范圍分區:
partition by range(t_time)
(
partition table_name_2015_1 values less than(to_date('01/01/2015','dd/mm/yyyy')),
partition table_name_2015_2 values less than(to_date('01/01/2015','dd/mm/yyyy')),
partition table_name_2015_3 values less than(to_date('01/01/2015','dd/mm/yyyy')),
partition table_name_2015_4 values less than(to_date('01/01/2015','dd/mm/yyyy')),
partition table_name_2015_5 values less than(to_date('01/01/2015','dd/mm/yyyy')),
partition table_name_2015_6 values less than(to_date('01/01/2015','dd/mm/yyyy')),
partition table_name_2015_7 values less than(to_date('01/01/2015','dd/mm/yyyy')),
partition table_name_2015_8 values less than(to_date('01/01/2015','dd/mm/yyyy')),
partition table_name_2015_9 values less than(to_date('01/01/2015','dd/mm/yyyy')),
partition table_name_2015_10 values less than(to_date('01/01/2015','dd/mm/yyyy')),
partition table_name_2015_11 values less than(to_date('01/01/2015','dd/mm/yyyy')),
partition table_name_2015_12 values less than(to_date('01/01/2015','dd/mm/yyyy'))
);
這樣按照月份刪除數據的時候,我們可以:
alter table table_name truncate partition table_name_2015_1;
或者:
alter table table_name drop partition table_name_2015_1;
還有人會問,truncate刪除的數據是不會留下undo日志的,不能進行數據回滾等操作。其實在實際開發中,歷史數據一般都會移植到另外的數據庫中進行保存,這種數據庫稱作為離線數據庫或者歷史數據庫。
只要我們在刪除表空間中數據前,先通過程序或者數據庫等操作方式將數據先備份到歷史數據庫中,在刪除表空間中的數據。
一般情況下,只有大數據的情況下才會用到表分區,只有幾萬條數據或者千單位的數據的時候,使用傳統的delete操作就可以了,性能上並不會有什么太大的區別。
文章參考了《讓ORACLE跑得更快2 基於海量數據的數據庫設計與優化》書中內容。