2020年9月21日 星期一

5.4 管理 Tablespace

當我們在一個 Tablespace 裡面建立一個 Table 時,會在這個 Tablespace 上面建立一個 Data Segment 給這個 Table 所使用,自Oracle 11g 開始出現了參數 deferred_segment_creation ,預設為 TRUE ,意思是在 Table 建立的當下且沒有任何資料時先不要配置 Data Segment 給它,直到有資料塞進來時再配置 Data Segment ,設為 FALSE 表示在 Table 建立的當下就配置 Data Segment ,這也是過往 Oracle DB 的作法, 11g 開始出現這個參數的用意只是為了節省 Tablespace 的使用空間,因為一個沒有資料的空 Table 在不配置 Data Segment 的情況下不會占用到 Tablespace 的空間。而隨著 Table 資料的增長,起始配置給這個 Table 的 Data Segment 勢必會遇到用完的一天,當 Data Segment 裡面的剩餘空間不足以再容納 Table 新增資料時,就必須從 Tablespace 裡面再挖一段 Extent 進來擴充:

那麼 Data Segment 在擴充的當下要怎麼知道 Tablespace 還有沒有足夠的空間來新增 Extent ? 這個時候就要提到 Tablespace 裡面的 Extent Management 機制,分為 Dictionary Management 與 Local Management。


  • Dictionary Management:

Tablespace 裡面的 Free Extent 由系統的 Table SYS.UET$ 與 SYS.FET$ 來管理,已使用的 Extent 記錄在 SYS.UET$ 裡面,Free Extent 則是記錄在 SYS.FET$ ,當 Segment 需要新增 Extent 時便會從 SYS.FET$ 來得知有哪些 Free Extent 可以用, SYS.FET$ 裡面記錄的某一筆 Free Extent 被拿去使用時,便會從 SYS.FET$ 裡面刪除這筆資料,同時會在 SYS.UET$ 新增一筆資料表示這個 Extent 已被使用。這種做法的缺點是會增加系統額外的 I/O 操作,因為除了前台對 Table 資料異動的 I/O 之外,後端也要不斷的對 FET$ 與 UET$ 進行操作,容易影響到整體的效能。


  • Local Management:

對於 Free Extent 的管理不使用 UET$ 與 FET$ ,而是以 bitmap 的方式直接在 Tablespace 所屬的 Data File Header 上直接標記有哪些空的 Block 可以做使用, Segment 需要新增 Extent 的時候可以直接從 Data File Header 得知可用空間然後馬上進行擴充,不用再去查找 FET$ 也不用對 FET$ 與 UET$ 進行操作,在流程上省去了很多動作,此種管理方式對整體效能較好。


在建立 Tablespace 的當下使用 extent management 關鍵字就可以選擇要使用哪一種管理方式了:

SQL> create tablespace ts1 datafile '/u01/app/oradata/ts1_01.dbf' SIZE 50M extent management dictionary default storage (initial 50K next 50K minextents 2 maxextents 50);

(建立 Extent Management 為 Dictionary 的 Tablespace

起始 segment 50K 不夠的時候以 50k 為單位擴充,最大可擴充 50 個 Extent)


SQL> create tablespace ts1 datafile '/u01/app/oradata/ts1_01.dbf' SIZE 50M extent management local;

 (建立 Extent Management 為 Local 的 Tablespace)


在 Local Management 下系統會自行決定下個 Extent 要增長的大小,預設以 64K 為單位增長,隨著 Segment 的擴充也有可能增加 Extent 的單位至 1M、8M 或是64M 。我們可以透過設定 Uniform size 來固定每次增加 Extent 的大小,例如每次固定增加 256K:

SQL> create tablespace ts1 datafile '/u01/app/oradata/ts1_01.dbf' SIZE 50M extent management local uniform size 256K;


現在 Oracle 預設的 Extent Management 都是 Local Management ,也就是不指定 Extent Management 時都是用 Local Management ,因為 Local Management 的管理方式與效能較好, Dictionary Management 已經不再使用了。透過 dba_tablespaces 可以得知目前 Tablespace 是使用哪一種 Extent Management:

轉換 Tablespace 為 Dictionary 或是 Local Management 可以使用dbms_space_admin 來達成:

SQL> exec dbms_space_admin.Tablespace_Migrate_TO_Local('ts1'); 

(將 Tablespace ts1 由 Dictionary Management 轉換為 Local Management)


SQL> exec dbms_space_admin.Tablespace_Migrate_FROM_Local('ts2');

(將 Tablespace ts2 由 Local Management 轉換為 Dictionary Management)


不過現在預設都是使用 Local Management ,也不太會有轉換的需求了。


如果要刪除一個 Tablespace ,使用 drop tablespace 命令:

SQL> drop tablespace ts1; 

(刪除 tablespace ts1)


SQL> drop tablespace ts2 including contents and datafiles;

(刪除 tablespace ts1 並且連同相對應的 Data File 也一併刪除)


除此之外,我們還可以使用 alter tablespace 命令來更改 tablespace 的屬性,例如將 Tablespace 更改為唯讀模式 (read-only) 或是 nologging :

SQL> alter tablespace ts1 read only; 

(將 tablespace ts1 改為 read-only)


SQL> alter tablespace ts1 read write; 

(將 tablespace ts1 改為 read-write)


SQL> alter tablespace ts2 nologging; 

(將 tablespace ts2 改為 nologging)


SQL> alter tablespace ts2 logging; 

(將 tablespace ts2 改為 logging)


Tablespace 建立起來預設都是 logging 模式,也就是在此 Tablespace 做的任何異動都會記錄到 redo log 當中,建議不要將 Tablespace 更改為 nologging ,因為 nologging 模式下, Tablespace 的異動不會記錄在 redo log ,只要發生異常就無法還原。我們有時候會為了加速 insert 的效能短暫的將 Tablespace 設定為 nologging ,不過在做完這個任務後必須馬上將它改回 logging 並且執行備份,避免造成無法還原的情況。

 

對於 Tablespace 的監控,最常使用的就是 dba_tablespaces 、 dba_free_space 與 dba_data_files 來查看狀態了,而 DBA 則是需要每天觀察 Tablespace 的使用率,我們可以使用下列語法來查詢:

col ts_name format a12

col type format a12

select a.tablespace_name ts_name,c.contents type,

          round((a.mbytes - nvl(b.mbytes,0))/a.mbytes * 100,2) "USED(%)",

          round(nvl(b.mbytes,0)/a.mbytes * 100,2) "free(%)",

          round(nvl(b.mbytes,0),2) "free(MB)",a.mbytes "total(MB)"

 from (select tablespace_name,sum(bytes)/1024/1024 mbytes

          from dba_data_files

        group by tablespace_name) a,

       (select tablespace_name,sum(bytes)/1024/1024 mbytes 

          from dba_free_space

        group by tablespace_name) b,

          dba_tablespaces c

where a.tablespace_name=b.tablespace_name(+)

  and a.tablespace_name=c.tablespace_name(+)

union

select a.tablespace_name ts_name,b.contents type,

          round(nvl(b.mbytes,0)/a.mbytes * 100,2) "USED(%)",

          round((a.mbytes - nvl(b.mbytes,0))/a.mbytes * 100,2) "free(%)",

          round((a.mbytes - nvl(b.mbytes,0)),2) "free(MB)",a.mbytes "total(MB)"

  from (select tablespace_name,sum(bytes)/1024/1024 mbytes

            from dba_temp_files group by tablespace_name) a,

        (select ss.tablespace_name,ts.contents,

                  sum((ss.used_blocks*ts.block_size))/1024/1024 mbytes

            from gv$sort_segment ss, dba_tablespaces ts

          where ss.tablespace_name = ts.tablespace_name

            group by ss.tablespace_name,ts.contents) b

 where a.tablespace_name=b.tablespace_name

 order by ts_name;

3-33


上述範例使用的是 dba_data_files 中的 bytes 欄位來計算使用率,如果 Data File 有設定 autoextend 的話,可以將 bytes 欄位更改為 maxbytes 來計算會更加的精確。以下範例是以 maxbytes 計算之:

col ts_name format a12

col type format a12

select a.tablespace_name ts_name,c.contents type,

 round((a.rmbytes - nvl(b.mbytes,0))/decode(a.mbytes,0,32767,a.mbytes) * 100,2) "USED(%)",

 100 - round((a.rmbytes - nvl(b.mbytes,0))/decode(a.mbytes,0,32767,a.mbytes) * 100,2) "free(%)",

 round(nvl(b.mbytes,0),2) "free(MB)",a.rmbytes "total(MB)"

from

 (select tablespace_name,sum(maxbytes)/1024/1024 mbytes,sum(bytes)/1024/1024 rmbytes from dba_data_files

   group by tablespace_name) a,

 (select tablespace_name,sum(bytes)/1024/1024 mbytes from dba_free_space

   group by tablespace_name) b,

  dba_tablespaces c

where a.tablespace_name=b.tablespace_name(+)

  and a.tablespace_name=c.tablespace_name(+)

union

select a.tablespace_name ts_name,b.contents type,

 round((a.rmbytes - nvl(b.mbytes,0))/decode(a.mbytes,0,32767,a.mbytes) * 100,2) "USED(%)",

 100 - round((a.rmbytes - nvl(b.mbytes,0))/decode(a.mbytes,0,32767,a.mbytes) * 100,2) "free(%)",

 round((a.rmbytes - nvl(b.mbytes,0)),2) "free(MB)",a.rmbytes "total(MB)"

from

 (select tablespace_name,sum(maxbytes)/1024/1024 mbytes,sum(bytes)/1024/1024 rmbytes

   from dba_temp_files group by tablespace_name) a,

 (select ss.tablespace_name,ts.contents,sum((ss.used_blocks*ts.block_size))/1024/1024 mbytes

   from gv$sort_segment ss,dba_tablespaces ts

  where ss.tablespace_name=ts.tablespace_name

  group by ss.tablespace_name,ts.contents) b

where a.tablespace_name=b.tablespace_name

order by ts_name;


這邊要注意的是,如果 Data File 沒有設定為 autoextend 屬性,則 dba_data_files 裡面的 maxbytes 欄位會顯示為 0 :



沒有留言:

張貼留言