Oracle Lob存儲及性能問題探討

 

 

1.LOB對象的分類
   Oracle 支持多種類型的大對象,用於存儲非結構化的數據,如圖片、文檔等內容。應用最爲普遍的是LOB 類型,LOB 4 種類型:
   --CLOB  字符大對象,用於替換較老的LONG 類型;
   --BLOB  二進制大對象,主要用於存儲二進制格式的大對象
   --NCLOB  基於國家語言字符集的字符大對象
   --BFILE  大對象存儲於數據庫外部的操做系統文件系統中
ZLHIS ZLBH 中都大量應用了LOB 類型來保存諸如,電子病歷、序列化對象、檢查圖像結果等信息; 文本討論的內容不包括BFILE 類型。
 
2. LOB 對象的存儲結構
  若是表中存儲在lob 對象,將會建立兩個新增的物理段結構:
  --LOBINDEX , LOB 索引用於快速定位LOB 段中的LOB 對象
  --LOBSEGMENT LOB
   能夠經過DBA_LOBS ALL_LOBS USER_LOBS 視圖來查看LOB 段的信息,例如:
 SQL> select column_name,segment_name,index_name
 2 from dba_lobs
 3 where table_name=' 病歷標記圖形 '
 4 and owner='ZLHIS'
 5 /
COLUMN_NAME          SEGMENT_NAME                   INDEX_NAME
-------------------- ------------------------------ -------------------
圖形                  SYS_LOB0000052398C00004$$      SYS_IL0000052398C00004$$
 
上述*_LOBS 視圖提供了下列的列:
OWNER               表全部者
TABLE_NAME           表名稱
COLUMN_NAME        LOB 列的名稱
SEGMENT_NAME       LOB 段名稱
INDEX_NAME          LOB Index 的名稱
CHUNK               顆粒大小( 單位爲字節)
PCTVERSION           PctVersion 參數( 後面有詳細介紹)
CACHE                LOB 段是否開啓CACHE (yes/no)
LOGGING              Logging 選項        (yes/no)
IN_ROW              是否開啓在行內存儲選項        (yes/no)
 
   在內部,Oracle 經過一個叫作定位器的指針來定位特定行對應的LOB 對象,在LOB 中有三種不一樣類型的結構:
   --Lob locator ,佔用20 個字節,存儲於表段中;
   --Lob Inode  ,最少16 字節,存儲於LOB Index 段中
   --Data array  ,真實的二進制數據;
  三種結構示意以下:    
3. LOB 存儲參數
3.1 表空間
   LOB 對象存儲在那一個表空間中,取決於你在Lob 對象中的storage 子句設置:
   -- 若是沒有爲LOB 指定表空間,則LOB 段與LOBIndex 都存儲到與表相同的表空間上;
   -- 若是指定了表空間,則LOB 段與LobIndex 都存儲到指定的表空間上;
 爲LOB 類型指定存儲表空間的例子以下:
   create table ZLHIS. 病歷標記圖形
  (
     編碼 VARCHAR2(4) not null,
    名稱 VARCHAR2(30),
    簡碼 VARCHAR2(10),
     圖形 BLOB )
    LOB( 圖形 )
    STORE AS 病歷標記圖形 _ 圖形 _LOB (
    TABLESPACE zl9eprlob
    pctversion 10 disable storage in row chunk 4k
    INDEX 病歷標記圖形 _ 圖形 _LOB_INDEX (
    TABLESPACE zl9eprlob )  )
 也能夠爲 LOB 段與 LOB Index 指定名稱,若是沒有指定則由 Oracle 自動生成一個惟一名稱。
 
3.2   存儲在行內仍是行外
   LOB 是否存儲在行內,經過enalbe storage in row disable storage in row 來進行控制,這個選項只能在表建立時指定, 存儲在行內的語法以下:
    STORE AS ( enable storage in row )
     若是LOB 數據小於4000 字節,ORACLE LOB 存儲於與表相同的段內; 實際上,最大的存儲在行內的LOB 大小是3964 字節。若是LOB 數據大於3964 字節將存儲到獨立的LOB 段上(也就是存儲在行外了)。
    若是LOB 對象大於3964 字節,且設置了」enable storage in row」 ,則LOB 列中在38 84 字節處存儲LOB 對象的控制數據。同時須要注意的是,行內存儲較大的LOB 對象,可能形成行遷移現象;好比,存儲3900 字節的LOB 對象到2k 的數據塊裏,這一行將存儲在2 個或更多的塊上;若是有大量的lob 存在這種現象,可能極大的影響LOB 的讀寫性能。
    一樣,若是存儲在行內,LOB 數據與標準的結構化數據在redo/undo 機制的上是徹底相同的, 關於這個內容後面還會有說明。
    存儲在行外的語法以下
    STORE AS ( disable storage in row )
    這個選項將全部的LOB 數據存儲到獨立的LOB 段上,而不論LOB 對象的大小。20 個字節的定位器(Locator )指針存儲在表的行裏,惟一標識存儲在LOB 段中的LOB 數據。LOB INDEX 包括了INODE LOCATOR ,這樣就有了LOB 段中的數據塊的映射關係,經過存儲在行內的LOCATOR LOB INDEX 來快速檢索特定的LOB 數據。
    存儲在行外的 LOB 數據最小的段分配單位是數據庫塊( DB_BLOCK_SIZE ),好比 8K ;這樣即便你的 LOB 對象很小,也將至少佔用一個數據塊;若是你的 chink size 設置爲大於塊的 1 倍大小,好比 10K, 一個 LOB 數據將佔用至少 2 個數據塊。
    存儲在行內的 LOB( 注意即便啓用了存儲在行內的特性,要存儲在行內也必須是小於 4000 字節的 LOB 對象 ) ,在事務中僅僅產生行中存儲的定位器 (LOCATOR) LOB INDEX UNDO 信息,相比存儲在行外的 LOB 對象就少了許多。存儲在行外的 LOB 數據的存取,則使用直接路徑的方式進行讀取,也就是繞過數據庫緩存來進行讀取。   
 
3.3  顆粒大小(CHUNK )
   指定 CHUNK 大小的語法以下:
   STORE AS ( CHUNK bytes )
   CHUNK 只能在建立時指定。CHUNK 的單位爲字節,只能是DB_BLOCK_SIZE 的倍數;也就是說最小爲數據塊的大小,Oracle 將自動取一個最接近DB_BLOCK_SIZE 倍數的大小。例如, db_block_size 設置爲2k 的狀況下,將chunk 設置爲3000 字節, chunk 將自動設置爲4096 字節。經過一個實驗來講明一下:
 
   -- 建表時,將 CHUNK 指定爲 3000 字節
   SQL> create table TEST_LOB
   2 (
   3    IMAGE BLOB
   4 )
   5   LOB(IMAGE)
   6    STORE AS TEST_IMAGE_LOB (
   7      TABLESPACE zl9eprlob
   8      pctversion 10 disable storage in row chunk 3000
   9      INDEX TEST_IMAGE_INDEX (
   10      TABLESPACE zl9eprlob)
   11    );
   Table created
   -- 查詢 LOB 視圖,看到 Oracle 自動設置 CHINK SIZE 8k ,也就是 1 倍數據塊的大小。
     SQL> select chunk
     2 from dba_lobs
     3 where table_name='TEST_LOB'
     4 and owner=USER
     5 ;
      CHUNK
     ----------
      8192
 
    須要注意, CHUNK 僅僅對存儲在行外的 LOB 數據有效。 從上面能夠看出, CHUNK DB_BLOCK_SIZE 肯定了存儲在行外的 LOB 數據的空間分配單位。例如, CHUNK 設置爲 32K ,且設置了 」disable storage in row」 ,即便 LOB 數據只有 10 字節大小,也將在 LOB 段中分配 32K 的空間。若是 chunk 設置不合理,就可能形成空間的巨大浪費 , 也會對性能形成很差的影響。
 
3.4  PCTVERSION
    咱們都知道Oracle 中有「一致性讀」的概念,事務提交前的「前鏡像數據」會存儲到UNDO 表空間中,若是查詢的開始時間晚於事務的開始時間,將從UNDO 表空間讀取修改前的數據,這特性被稱爲「一致性讀(Consistent Read) 」。前面說過對存儲在行外的LOB 對象,LOB 數據自己是不生產UNDO 信息的,那Oracle 如何保證LOB 對象的讀一致性呢?Oracle 爲解決這個問題,專門引入了PCTVERSION 參數,語法以下:
    STORE AS ( PCTVERSION n )
    這個參數能夠在建立以後進行修改,PCTVERSION 用於控制LOB 保存前鏡像數據存儲空間的百分比,保存的這些前鏡像數據就用於LOB 對象的一致性讀;這些數據保存在與LOB 段相同的表空間中,若是設置太大將使得表空間膨脹得很快。
   若是一個會話嘗試讀取一個被覆蓋的LOB 前鏡像數據(緣由就是pct version 設置得過小),一樣將產生」ora-01555: 快照太舊的錯誤。須要說明的是pctversion 只是一個大概值,Oracle 內部有一個特殊的算法來計算保留的空間。若是系統中有針對LOB 的大量的長時間事務,就須要結合各類狀況,設置一個合理的值。
 
3.5 LOB 數據的緩存
   Oracle 使用數據庫調整緩存來加快數據的存取速度,LOB 數據與結構化數據的緩存機制也不同,這須要引發咱們的注意,與普通的段同樣,也能夠指定CACHE NOCACHE 選項:
   STORE AS ( CACHE ) /  STORE AS ( NOCACHE )
   這個選項也能夠在建立表以後經過 alter 語句進行修改。 缺省設置爲 NOCACHE ,在 NOCACHE 下,讀寫 LOB 數據將使用直接路徑讀寫,繞過數據庫高速緩存 , 直接從磁盤進行讀取;這意味着 Oracle 將不緩存 LOB 段的數據塊;若是在 AWR/STATSPACK 中發現direct path read/ direct path wirte 等待很高,就多是 LOB 對象 的讀寫引發的。
   設置爲CACHE ,則LOB 的讀寫則將經過數據庫調整緩存進行; 這種方式下讀取LOB 的等待事件顯示爲 "db file sequential read", 可是不象掃描表的數據塊那樣置於 LRU 列表的「最近使用」端的末尾。
    設置爲 cache 選項必定要很是當心,若是表中的 LOB 段很大,緩存這些 LOB 段就須要消耗大量的內存;若是表的 lob 段總的空間不大,且讀寫頻繁,設置爲 cache 就比較恰當。例如,咱們在實際調優過程當中,將 ZLBH 資源信息的 RESOURCEINFO 表中的序列化窗體的 LOB 列設置爲 cache 就取得了比較好的效果。
     存儲在行內的 LOB 列是不受 CACHE/NOCACHE 選項影響的,存儲在行內的 LOB 數據同普通數據塊同樣,經過數據庫高速緩存進行讀寫。 CACHE/NOCACHE 選項也會對 REDO 日誌的數量產生影響。 NOCACHE 時,直接路徑讀寫 LOB 對象,整個 LOB 塊映象會寫入 REDO ; 而設置 CACHE , 僅僅 LOB 數據塊變化時,纔會寫入到 REDO 中。例如,設置了 'DISABLE STORAGE IN ROW NOCACHE CHUNK 32K' 的情 況下,即便 LOB 數據僅僅只有 5 個字節,也將會產生全部 32K redo 記錄,若是是 cache 選項,則只產生 5 字節的 redo 記錄。
 
3.6 LOGGING
    咱們知道 Oracle 在表、索引等數據段上,可使用 nologging 選項,來減小 REDO 產生的量,以達到提高性能的目的。在 LOB 類型中,這個選項必須依賴於 NOCACHE 選項,只有下面兩種組合:
STORE AS ( NOCACHE  LOGGING )
STORE AS ( NOCACHE  NOLOGGING )
    也就是說在 CACHE 選項下, Oracle 是強制使用 LOGGING 方式的;這個值缺省爲 LOGGING 。若是啓用了 NOLOGGING ,當發生數據損壞 ,Oracle 須要作介質恢復時, LOB 段將被標記爲損壞,也就是損失的 LOB 數據是沒法恢復的,緣由就是沒有產生恢復必須的 REDO 日誌。   
 
3.7 結語
    若是更新存儲在行外的LOB 數據,將會建立一個版本的LOB 前鏡像數據,這些數據是要消耗必定的存儲空間,前已有所述,這是爲了實現「一致性讀」的須要,也就是說LOB 段須要消耗比結構化數據更多的空間,才能實現事務的併發。同時在開發時,咱們能夠結合 LOB 對象的CHUNK 大小,設置數據庫驅動中一次讀取LOB 對象的緩存大小。
   在不一樣的應用場景 ,須要分析咱們的應用並結合 LOB 平均大小、數據塊大小、 Oracle 的內存設置等因素來綜合考慮咱們的 LOB 設置。
   但願本文能給你正確使用 LOB 類型帶來幫助 !
參考資料:
  Metalink: Oracle8 Example SQL Demonstrating use of LOBs in Oracle8 [ID 47740.1]
  Metalink: LOBs and ORA-01555 troubleshooting [ID 846079.1]
相關文章
相關標籤/搜索