數據表創建參數介紹

本文轉自http://blog.itpub.net/17203031/viewspace-688011/



創建數據表create table是我們對數據庫進行的常見操作。我們一般使用create table之後,指定了數據列信息和主鍵等約束信息,其他就交給Oracle使用默認值了。今天我們一起來看看這些默認值。說明:本片只關注一般數據表,臨時表、聚簇、IOT等特殊類型暫時不考慮。

 

提取完整的DDL

 

首先我們需要提取出創建數據表的完整DDL語句,才能將Oracle提供的默認值們正確抽取出來。此處我們使用dbms_metadata.get_ddl方法。

//源數據抽取腳本

set serveroutput on size 10000;

set timing on;

 

declare

  c_ddl clob;

begin

  c_ddl := dbms_metadata.get_ddl('TABLE','DEPT','SCOTT');

 

  dbms_output.put_line(c_ddl);

end;

/

//輸出結果

  CREATE TABLE "SCOTT"."DEPT"

   (    "DEPTNO" NUMBER(2,0),

    "DNAME" VARCHAR2(14),

    "LOC" VARCHAR2(13),

     CONSTRAINT "PK_DEPT" PRIMARY KEY ("DEPTNO")

  USING INDEX PCTFREE 10 INITRANS 2 MAXTRANS 255 COMPUTE STATISTICS

  STORAGE(INITIAL 65536 NEXT 1048576 MINEXTENTS 1 MAXEXTENTS 2147483645

  PCTINCREASE 0 FREELISTS 1 FREELIST GROUPS 1 BUFFER_POOL DEFAULT)

  TABLESPACE "USERS"  ENABLE

   )

PCTFREE 10 PCTUSED 40 INITRANS 1 MAXTRANS 255 NOCOMPRESS LOGGING

  STORAGE

(INITIAL 65536 NEXT 1048576 MINEXTENTS 1 MAXEXTENTS 2147483645

  PCTINCREASE 0 FREELISTS 1 FREELIST GROUPS 1 BUFFER_POOL DEFAULT)

  TABLESPACE "USERS"

 

 

上面是使用Oracle自帶的源數據導出工具,對Scott用戶下的數據表EMP進行提取的DDL語句。

 

 

注意:其上有兩個片段(標註紅色)是相似。這個要說明一下,我們建立一個數據表,在Oracle中要建立數據段data segment對象。同時,如果我們指定了主鍵,Oracle會自動爲這個主鍵建立索引,索引是一種索引段index segment對象。所以,如果在建立數據表的時候,也指定了主鍵primary key,那麼一共會生成兩個段segment對象。

 

下面,我們對相似的代碼參數片段進行分析。

 

1、PCTFREE 10 PCTUSED 40 INITRANS 1 MAXTRANS 255 NOCOMPRESS LOGGING

2、STORAGE

(INITIAL 65536 NEXT 1048576 MINEXTENTS 1 MAXEXTENTS 2147483645

  PCTINCREASE 0 FREELISTS 1 FREELIST GROUPS 1 BUFFER_POOL DEFAULT)

3、TABLESPACE "USERS"

 

 

代碼段可以分爲三個部分,下面分別進行介紹。

 

Part1、數據塊Data Block與表屬性部分

 

標註1部分表示是數據塊結構和使用參數,以及我們的數據表在使用中的一些參數。

 

在Oracle中,數據庫邏輯結構被劃分爲“表空間Tablespace、段對象Segment、分區extent和塊block”。其中,block是最小的結構對象。

 

ü        PCTFREE:爲數據更新準備的“留白”

 

數據終究是要寫入到數據塊裏面的。對數據表中的塊block來說,都會依次填滿行數據。而Oracle寫數據表的順序是首先找到一個空閒塊,之後向空閒塊中寫入數據。

 

那麼Oracle如何判斷這個數據塊是否非空閒呢?就是使用PCTFREE參數了,該參數是一個百分比值,默認爲10%。如果一個數據塊空閒的空間低於PCTFREE設定值,就認爲這個數據塊已經寫滿。會將這個塊從空閒塊的列表(FREELIST)上取下來。

 

那麼,爲什麼要有這個參數?爲什麼要保留這10%呢?答案就是爲了進行update使用。數據保存在數據行裏,隨着不斷的更新,每行所佔有的空間是一個不定的範圍。在數據插入之後,如果發生存儲空間增加(比如:varchar2類型字符長度變化),餘下的10%就留作數據行空間延展使用。

 

那麼,如果超過這個10%,還是不能裝下數據怎麼辦?Oracle定位數據使用的物理rowid機制,實際上就是定位特定的數據行在某個數據塊的第幾個slot(槽)上。如果一個數據塊再也裝不下一些數據行,那麼這些數據行就需要另找一個新的數據塊進行保存。也就意味着這些行有一個新的new-rowid位置。但是,Oracle還會按照原來的old-rowid定位數據行。原來的數據塊上,記錄着該數據行的新位置new-rowid,再次找到新的數據行位置。這個技術過程就稱爲行遷移(row migrate)。

 

注意:我們要讀一個數據行的內容。理論上希望訪問的數據塊越少越好,最好只有一個塊。但是,如果發生行遷移,我們就不得不訪問多個數據塊才能得到數據。所以,行遷移是我們通常不希望看到的。解決的方法,就是保留一個適當的PCTFREE值。

 

 

選擇合適的PCTFREE的值要根據系統的性質而定。如果是一個典型的OLTP系統,數據更新操作多,變化大。這時候就需要設置一個略大些的PCTFREE。相反,如果更新很少,大部分都是讀操作,PCTFREE略小些也無礙。

 

 

PCTFREE意味着一種空間的閒置。如果需要進行某種數據表壓縮,設置PCTFREE爲0也是一種思路。

 

 

ü        PCTUSED:數據塊的判定容量底線

 

剛纔我們談論PCTFREE的時候,介紹了Oracle在寫入數據的時候,是將新數據寫入到數據塊中,直到認爲已經滿了後,再尋找新塊寫入。這個過程相反的是,如果一個過去寫滿的數據塊,經過若干次刪除後,會逐漸變空,那麼什麼時間點才能認爲這個塊是一個空閒塊,能接受新的數據插入呢?

 

這就是PCTUSED參數的用途。如果一個數據塊的使用容量低於PCTUSED設置的限制,那麼就認爲這個數據塊已經空閒,可以放置回FREELIST列表中,作爲空閒數據塊接受新數據行的插入。

 

 

一般是不需要調節這個參數的,筆者認爲,頻繁的數據塊空閒或者寫滿切換,是有損於數據庫性能的。所以,設置默認的40%一般是可以接受的。對一些數據變化巨大,刪除頻繁的系統,這個參數可以配合PCTFREE統一籌劃。

 

 

ü        INITRANS和MAXTRANS

 

初始事務INITRANS和最大事務MAXTRANS是在數據塊級別的參數。Oracle行級鎖是Oracle最大的特點之一。數據庫事務的本質還是對數據塊的修改,而事務的信息是記錄在數據塊頭。

 

參數INITRANS的作用就是表示數據塊上可以標記的初始事務數目。如果同時進行的事務數據量超過這個數量,事務數目可以增加,直到達到MAXTRANS的限制。

 

 

從性能角度看,我們不希望一個數據塊同時進行過多的事務。因爲這樣起碼意味着數據塊過熱。所以,建議設置一個合理的INITRANS。

ü        COMPRESS壓縮參數

 

Compress參數含義很清楚:就是在存儲數據表數據的時候是否啓用壓縮選項。壓縮使用的級別是數據塊block級別。Oracle對數據塊的壓縮採用相鄰相同值合併的壓縮算法。

 

Compress參數有兩個系列參數:

 

1、OMPRESS FOR DIRECT_LOAD OPERATIONS:作用於Compress相同,適合於數據倉庫OLAP系統。只在直接插入過程中在表或者分區上啓用壓縮技術;

2、COMPRESS FOR ALL OPERATIONS:適合於OLTP系統,針對所有的操作均啓用了壓縮選項。要求的版本較高。

 

下面,我們通過一個小實驗,來確定壓縮效果。首先,構建兩個數據表test和test1,數據量和順序相同,只是參數不同。

 

SQL> conn scott/tiger@orcl;

Connected to Oracle Database 10g Enterprise Edition Release 10.2.0.1.0

Connected as scott

 

SQL> set timing on;

SQL> create table test nocompress as select * from all_objects where 1=0;

Table created

Executed in 0.11 seconds

 

SQL> insert /*+append */ into test select * from all_objects;

40724 rows inserted

(提交和重複執行該語句,篇幅原因省略

 

SQL> insert into test  select * from test;

122170 rows inserted

Executed in 8.412 seconds

 

SQL> select count(*) from test;

  COUNT(*)

----------

    244340

Executed in 0.872 seconds

 

SQL> exec dbms_stats.gather_table_stats('SCOTT','TEST',cascade => true);

PL/SQL procedure successfully completed

Executed in 2.354 seconds

 

//壓縮數據表

SQL> create table test1 compress as select * from all_objects where 1=0;

Table created

Executed in 0.17 seconds

 

SQL> insert /*+append */ into test1 select * from all_objects;

40724 rows inserted

Executed in 2.724 seconds

(提交和重複執行該語句,篇幅原因省略

 

SQL> insert into test1  select * from test1;

122172 rows inserted

Executed in 1.562 seconds

 

SQL> commit;

Commit complete

Executed in 0.03 seconds

SQL> select count(*) from test1;

 

  COUNT(*)

----------

    244344

 

Executed in 0.03 seconds

 

SQL> exec dbms_stats.gather_table_stats('SCOTT','TEST1',cascade => true);

 

PL/SQL procedure successfully completed

 

Executed in 0.501 seconds

 

 

兩個數據表數據量和結構完全相同,內容也相同。經過分析後,我們檢查數據字典反應的情況。

 

SQL> select segment_name, segment_type, bytes/1024/1024 MB, blocks, extents from user_segments where  segment_name in ('TEST','TEST1');

 

SEGMENT_NA SEGMENT_TY         MB     BLOCKS    EXTENTS

---------- ---------- ---------- ---------- ----------

TEST1      TABLE              17       2176         32

TEST       TABLE              28       3584         43

 

Executed in 0.08 seconds

 

 

結果很清楚了,在沒有使用壓縮技術的TEST數據表,佔有空間28MB,共3584個數據塊。而採用過數據庫壓縮技術後,空間使用到了17MB,共2176個數據塊,空間節省39%左右。

 

進一步實驗,根據Oracle壓縮的方法,我們嘗試將近似的行之間相同數據情況增加。all_objects視圖中,owner和object_type相似度較高,選擇性低,這樣我們組織數據形態。

 

//構建實驗數據表三

SQL> create table test2 compress as select * from test1 order by owner,object_type;

 

Table created

Executed in 5.719 seconds

 

SQL> exec dbms_stats.gather_table_stats(user,'TEST2',cascade => true);

 

PL/SQL procedure successfully completed

Executed in 1.102 seconds

 

SQL> select segment_name, segment_type, bytes/1024/1024 MB, blocks, extents from user_segments where  segment_name in ('TEST','TEST1','TEST2');

 

SEGMENT_NA SEGMENT_TY         MB     BLOCKS    EXTENTS

---------- ---------- ---------- ---------- ----------

TEST1      TABLE              17       2176         32

TEST       TABLE              28       3584         43

TEST2      TABLE              10       1280         25

 

Executed in 0.08 seconds

 

 

按照算法特徵進行整理之後,數據表大小便爲了10M,壓縮率提高到了64%。

 

ü        LOGGING:日誌記錄

LOGGING參數表示對數據表或者其他對象進行變化性操作的時候,是否計入到REDO日誌中。默認情況下,Oracle對數據庫對象的任何修改性操作都會計入到redo log中,作爲數據庫恢復使用。開啓logging功能,是保證數據庫數據一致性的重要一步。

 

有一些時候,我們可能會不希望記日誌操作。因爲logging的時候會存在相當的性能損失,爲了避免這種損失,加快操作速度,我們可能會選擇暫時性的設置數據表爲nologging狀態。比如,我們在進行大規模數據加載的時候,可能就會選擇這種方式。

 

 

先不論nologging的好壞,有幾個問題筆者需要說明。首先,啓用數據表的nologging絕不意味着說一點重做日誌都不書寫。在運行過程中,數據表對應的索引對象等內容,都有一個重建的過程,這個過程是需要寫入redo log的。第二,不啓用redo log,的確會帶來一定的性能提升。但是沒有日誌的數據庫是極其危險的,一旦發生實例崩潰或者數據庫崩潰這種情況,數據庫數據丟失和不一致的情況是可能發生的。這種方法得不償失。


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章