表擴展失敗(ORA-01653)後的空間管理問題【THE SPACE MANAGEMENT PROBLEM OF THE TABLE EXTEND FAILD 】
這兩天在公司做ORACLE10G的DATAGUARD測試的時候,發現表擴展報錯後,後續的一些空間問題。
測試環境中,建了一個500M的TABLESPACE命名為TEST。
導入一張表(TEST_ALL)有280多萬的數據,然後建立了有建立了一個同樣結構的表TEST1,進行批量插入操作。
SQL> insert into test1 select * from test_all;
ORA-01653: unable to extend table TEST.TEST1 by 1024 in tablespace TEST
這是個較為常見的錯誤,就是TABLESPACE沒有空間了。以下為ORACLE給出的錯誤解釋和解決方法。
// *Cause: Cannot shrink the segment because it is not in auto segment space
// managed tablespace or it is not a data, index or lob segment.
// *Action: Check the tablespace and segment type and reissue the statement
因為我的表空間沒有設置自動增長,所以會有錯誤報出。但是接下來的問題就很有意思了。我們發現雖然插入操作失敗,但是其所佔的空間並沒有自動收回!我的測試環境中,TEST1表一直佔用184M左右的表空間。並且無論我發ROLLBACK,COMMIT,關閉當前SESSION甚至重新啟動數據庫,這個TEST1表一直會佔用著184M的表空間!
我只能手工TRUNCATE TABLE或者MOVE的方法來消減這張表佔用的空間。
有意思的是,當我利用ORACLE10G的新方法來試圖回縮所佔用的空間時,ORACLE報錯並且錯的讓我「找不到北」。如下:
SQL> alter table test1 shrink space cascade;
alter table test1 shrink space cascade
ORA-10635: Invalid segment or tablespace type
這個10635錯誤也是一個比較典型的錯誤,ORACLE給出的官方解釋:
// *Cause: Cannot shrink the segment because it is not in auto segment space
// managed tablespace or it is not a data, index or lob segment.
// *Action: Check the tablespace and segment type and reissue the statement
當時我以為自己沒有使用ORACLE的ASSM,但是經過確認自己建表空間的SQL,發現我使用的是ASSM的,但還是害怕有問題,對另外一張TEST_ALL的表進行操作,結果正常。
SQL> alter table test.test_all shrink space cascade;
Table altered
Executed in 10.859 seconds
看上去ORACLE報的這個錯誤也「驢唇不對馬嘴」了。讓我已經久已不用的MOVE命令關鍵時刻還是有點作用的。
這佔用的表空間只是會造成空間的浪費嗎,還是有其他副作用的。我先後做了兩個COUNT的SQL,其顯示的執行結果比較清楚的說明了這個問題。
第一個COUNT的SQL是沒有執行TRUNCATE TABLE命令前的,如下
SQL> select count(*) from test1;
COUNT(*)
----------
0
Execution Plan
----------------------------------------------------------
Plan hash value: 3896847026
--------------------------------------------------------------------
| Id | Operation | Name | Rows | Cost (%CPU)| Time |
--------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 5135 (1)| 00:01:02 |
| 1 | SORT AGGREGATE | | 1 | | |
| 2 | TABLE ACCESS FULL| TEST1 | 1 | 5135 (1)| 00:01:02 |
--------------------------------------------------------------------
Note
-----
- dynamic sampling used for this statement
Statistics
----------------------------------------------------------
6 recursive calls
2 db block gets
23641 consistent gets
0 physical reads
228 redo size
410 bytes sent via SQL*Net to client
385 bytes received via SQL*Net from client
2 SQL*Net roundtrips to/from client
0 sorts (memory)
0 sorts (disk)
1 rows processed
第二個COUNT的SQL是在執行MOVE TABLE後來完成的
SQL> select /*truncate*/count(*) from test1;
COUNT(*)
----------
0
Execution Plan
----------------------------------------------------------
Plan hash value: 3896847026
--------------------------------------------------------------------
| Id | Operation | Name | Rows | Cost (%CPU)| Time |
--------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 2 (0)| 00:00:01 |
| 1 | SORT AGGREGATE | | 1 | | |
| 2 | TABLE ACCESS FULL| TEST1 | 1 | 2 (0)| 00:00:01 |
--------------------------------------------------------------------
Note
-----
- dynamic sampling used for this statement
Statistics
----------------------------------------------------------
1 recursive calls
0 db block gets
4 consistent gets
0 physical reads
0 redo size
410 bytes sent via SQL*Net to client
385 bytes received via SQL*Net from client
2 SQL*Net roundtrips to/from client
0 sorts (memory)
0 sorts (disk)
1 rows processed
第二句要明顯好於第一句。性能上還有有差別的。即TEST1表的HWM是上升的。
接下來另外一個問題就出現了:如果我可以繼續插入的話,空間佔用會持續增加嗎即HWM會持續增加嗎?如果是的話,這樣導致的性能問題就大了。然後我正常插入了32000條數據,空間佔用沒有變化。為了防止是因為空間限製造成空間佔用不增加,我調大了表空間到600M,結果相同。即空間佔用仍為184M。還好,ORACLE沒有犯錯誤。現在比較兩種情況。
情況一:在不回縮空間佔用的情況下,執行COUNT(*)
SQL> SELECT /*320000*/ COUNT(*) FROM TEST1 WHERE OWNER='SYS';
COUNT(*)
----------
140216
Execution Plan
----------------------------------------------------------
Plan hash value: 3896847026
----------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
----------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 17 | 5144 (1)| 00:01:02 |
| 1 | SORT AGGREGATE | | 1 | 17 | | |
|* 2 | TABLE ACCESS FULL| TEST1 | 158K|
2628K| 5144 (1)| 00:01:02 |
----------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
2 - filter("OWNER"='SYS')
Note
-----
- dynamic sampling used for this statement
Statistics
----------------------------------------------------------
6 recursive calls
1 db block gets
23639 consistent gets
0 physical reads
176 redo size
413 bytes sent via SQL*Net to client
385 bytes received via SQL*Net from client
2 SQL*Net roundtrips to/from client
0 sorts (memory)
0 sorts (disk)
1 rows processed
第二種情況,先進行空間收縮,然後進行COUNT查詢。這次使用SHRINK操作,ORACLE沒有報錯。回縮後,TEST1表空間佔用31.8M
SQL> SELECT /*shrinked*/ count(*) from test1 where owner='SYS';
COUNT(*)
----------
140216
Execution Plan
----------------------------------------------------------
Plan hash value: 3896847026
----------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
----------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 17 | 890 (2)| 00:00:11 |
| 1 | SORT AGGREGATE | | 1 | 17 | | |
|* 2 | TABLE ACCESS FULL| TEST1 | 137K|
2288K| 890 (2)| 00:00:11 |
----------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
2 - filter("OWNER"='SYS')
Note
-----
- dynamic sampling used for this statement
Statistics
----------------------------------------------------------
0 recursive calls
0 db block gets
4008 consistent gets
0 physical reads
0 redo size
413 bytes sent via SQL*Net to client
385 bytes received via SQL*Net from client
2 SQL*Net roundtrips to/from client
0 sorts (memory)
0 sorts (disk)
1 rows processed
不難發現性能上,第二句還是有非常大的優勢的。
綜上所述
如果我們在操作(INSERT)某個表,因為空間擴張失敗後,需要認真執行空間的回縮處理。雖然在我們看來,失敗了回滾即可。但是其HWM卻上去後就沒有下來。也許這是ORACLE在並發處理情況下要求快速相應的一種策略。但是也會留下性能憂患的伏筆。週期性的執行空間管理還是非常非常有必要的。
但是對於ORACLE的SHRINK操作,只有對批量INSERT某個空表而造成空間擴展失敗後的表進行回縮時報ORA-10635錯來看,這應該是個BUG。(因為如果直接對某個空表或有原表中有些數據然後進行INSERT操作造成空間擴展失敗的表執行SHRINK操作都沒有問題)
要多想些,再多想些 -:)
--------------------系統環境
OS: REDHAT AS4 U5
ORACLE:10.2.0.1
歡迎指正。
原文出處: 表扩展失败(ORA-01653)后的空间管理问题【THE SPACE MANAGEMENT PROBLEM OF THE TABLE EXTEND FAILD 】-Be the miracle!-51CTO博客