Varchar竟然會自動存儲成lob類型?

​講師介紹

吳海存,10g / 11g / 12c OCM,Oracle Exadata / Golden Gate專家,曾於Amazon和Oracle公司擔任全球業務資深DBA,目前供職於中國農業銀行,負責數據庫前沿技術研究和支持。

 

本文主要是通過對底層數據塊進行DUMP分析的方式,介紹了varchar如何被自動存儲成了lob類型,該情況在某些業務場景下會產生大量的lob讀寫操作,從而造成數據庫性能問題。本文不會對varchar以及lob的使用和存儲機制做過多贅述。

 

我們知道,在Oracle數據庫中常使用varchar2來存儲字符串,而使用lob來存儲一些較大的數據對象,比如二進制或十六進制的信息文件、圖像、音頻等,都可以使用lob類型進行存儲。

 

varchar2是以字節byte爲單位存儲在數據塊中,而lob在存儲的時候,若lob列數據小於4000 bytes時,則Oracle會將其和本行的其他列一起,存儲在本行的數據塊中;若lob列數據大於4000 bytes,則Oracle會將其保存在logsegment中,在數據行該lob列的位置上會存儲一個指針信息,記錄實際存儲該lob列數據的lobsegment位置,此時lob的存儲是以chunk爲單位,chunk的大小爲標準數據塊(db_block_size)大小的整數倍,最大爲32K,一個chunk只能保存一行lob對象的數據,如果出現填不滿的情況,空閒空間是被置空的。

 

對於存儲在lobsegment中的lob數據,是否會經過buffer cache進行緩存呢?

 

這個是由lob的存儲參數cache/nocache進行控制,若設置不合理,可能會造成較嚴重的數據庫性能問題,比如設置爲nocache時,Oracle對該lob列讀寫時會繞開buffer cache,直接使用direct read/write的方式,可能會造成較高的物理IO負載;而設置爲cache時,則在對該Lob列進行讀寫時會使用buffer cache進行Lob chunk的緩存,在該情況下有可能造成大量的buffer被從LRU chain上age out出內存,從而影響數據庫性能。

 

文中如有疏漏之處,望指正!

 

【環境版本信息】  

 

Oracle版本:12.1.0.2

OS版本:CentOS 7.5

 

【驗證測試】  

 

在Oracle 12c之前的版本中,varchar2數據類型的最大長度爲4000 bytes。從12c的版本開始,引入了extended data type的字段類型,簡單地講,就是可以直接定義varchar2、nvarchar2和raw數據類型的長度爲最大32767 bytes(即32k) 了,方便用戶對較長的字符串進行存儲。該功能是通過參數max_string_size來進行控制,該參數可以取值如下:

 

  • standard:表示12c之前的標準長度,即varchar2、nvarchar2最大爲4000 bytes,raw最大爲2000 bytes;

  • extended:表示varchar2、nvarchar2、raw的最大長度可以指定爲32k bytes。

 

當使用extended data type的字段時,在數據庫的底層又是如何存儲的呢?我們通過如下案例來進行驗證。

 

1確認一下相應的用戶下沒有lob信息

 

 

SQL> select table_name,COLUMN_NAME,INDEX_NAME,CHUNK,CACHE from user_lobs;

no rows selected

 

2創建相應的表

 

 

SQL> create table test_extend_type 

                   (id number, 

                    name varchar2(4000), 

                    detail_info varchar2(32767));    --此處定義爲擴展的數據類型

 

3查看lob信息

 

 

SQL> set linesize 200 pagesize 200

SQL> col TABLE_NAME format a25

SQL> col COLUMN_NAME format a25

SQL> col INDEX_NAME format a25

SQL> select table_name,COLUMN_NAME,INDEX_NAME,CHUNK,CACHE from user_lobs;

TABLE_NAME                COLUMN_NAME               INDEX_NAME                     CHUNK CACHE

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

TEST_EXTEND_TYPE          DETAIL_INFO               SYS_IL0000111114C00003$$        8192 YES

 

可以發現,數據庫自動爲擴展類型的detail_info列創建了一個lob segment。

 

4對detail_info列插入不超過4000 bytes的數據

 

 

SQL> insert into TEST_EXTEND_TYPE values(1111,'aaaa','bbbbbb');

1 row created.

SQL> commit;

Commit complete.

 

5Dump出數據塊結構

 

 

SQL> select dbms_rowid.ROWID_RELATIVE_FNO(rowid),dbms_rowid.ROWID_BLOCK_NUMBER(rowid)                                     

  2      from TEST_EXTEND_TYPE where id=1111;

DBMS_ROWID.ROWID_RELATIVE_FNO(ROWID) DBMS_ROWID.ROWID_BLOCK_NUMBER(ROWID)

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

                                   2                                  133

SQL> alter system dump datafile 2 block 133;    

System altered.

SQL> oradebug setmypid;

Statement processed.

SQL>  oradebug tracefile_name;

/u01/app/oracle/diag/rdbms/orcl/orcl/trace/orcl_ora_24291.trc

 

如下圖所示,列detail_info存儲爲SecureFile的Blob類型,由於長度並不超過4000 bytes,所以保存爲DataInRow:

 

 

備註:Oracle在存儲Lob時,分爲BasicFiles和SecureFiles兩種存儲結構。後者是11g引入的,支持加密、壓縮和去重等功能,12c開始默認創建爲SecureFiles格式。

 

6對detail_info插入超過4000 bytes數據

 

 

SQL> insert into TEST_EXTEND_TYPE values(202020,'Sam_testing',lpad('TEST',500000,'SAM'));  

1 row created.

SQL> commit;

Commit complete.

SQL> select dbms_rowid.ROWID_RELATIVE_FNO(rowid),dbms_rowid.ROWID_BLOCK_NUMBER(rowid)                                     

        from TEST_EXTEND_TYPE where id=202020;

DBMS_ROWID.ROWID_RELATIVE_FNO(ROWID) DBMS_ROWID.ROWID_BLOCK_NUMBER(ROWID)

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

                                   2                                  133

 

7Dump出數據塊結構進行對比

 

 

SQL> alter system dump datafile 2 block 133;  

System altered.

SQL> oradebug setmypid;

Statement processed.

SQL> oradebug tracefile_name;

/u01/app/oracle/diag/rdbms/orcl/orcl/trace/orcl_ora_30873.trc

 

如下圖所示,由於數據長度超過了4000 bytes,直接被存儲到了SecureFile的Blob Segment中:

 

 

通過如上分析,我們可以得出如下結論:

 

  • 當定義列爲extended data type時,Oracle會自動創建一個SecureFile的Lob segment;

  • 當該字段存儲不超過4000 bytes的數據時,數據是和數據行其他的列一起保存在data block中的,以數據塊爲存儲單位;

  • 當改字段存儲超過4000 bytes的數據時,數據是存放Lob segment中的,以chunk爲單位,在數據塊上會存儲一個locator指針,指向實際存儲該列數據的chunk位置;

  • 當沒有創建lob字段而數據庫中有大量的direct path read/write(lob)等待事件時,需要檢查一下是否啓用了extended data type並且發生了自動存儲轉換,若是,則需要對相應的Lob segment進行調優。

 

附:如下是啓用extended data type的步驟:

 

 

SQL> conn / as sysdba

Connected.

SQL> shutdown immediate

Database closed.

Database dismounted.

ORACLE instance shut down.

SQL> 

SQL> startup upgrade

ORACLE instance started.

Total System Global Area 5368708176 bytes

Fixed Size                  8907856 bytes

Variable Size             956301312 bytes

Database Buffers         4395630592 bytes

Redo Buffers                7868416 bytes

Database mounted.

Database opened.

SQL> alter system set max_string_size=extended;

System altered.

SQL> @?/rdbms/admin/utl32k.sql

Session altered.

Session altered.

DOC>#######################################################################

DOC>#######################################################################

DOC>   The following statement will cause an "ORA-01722: invalid number"

DOC>   error if the database has not been opened for UPGRADE.

DOC>

DOC>   Perform a "SHUTDOWN ABORT"  and

DOC>   restart using UPGRADE.

DOC>#######################################################################

DOC>#######################################################################

DOC>#

no rows selected

DOC>#######################################################################

DOC>#######################################################################

DOC>   The following statement will cause an "ORA-01722: invalid number"

DOC>   error if the database does not have compatible >= 12.0.0

DOC>

DOC>   Set compatible >= 12.0.0 and retry.

DOC>#######################################################################

DOC>#######################################################################

DOC>#

PL/SQL procedure successfully completed.

Session altered.

2 rows updated.

Commit complete.

System altered.

PL/SQL procedure successfully completed.

Commit complete.

System altered.

Session altered.

Session altered.

Table created.

Table created.

Table created.

Table truncated.

0 rows created.

PL/SQL procedure successfully completed.

STARTTIME

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

09/25/2020 15:00:33.678157000

PL/SQL procedure successfully completed.

No errors.

PL/SQL procedure successfully completed.

Session altered.

Session altered.

0 rows created.

no rows selected

no rows selected

DOC>#######################################################################

DOC>#######################################################################

DOC>   The following statement will cause an "ORA-01722: invalid number"

DOC>   error if we encountered an error while modifying a column to

DOC>   account for data type length change as a result of enabling or

DOC>   disabling 32k types.

DOC>

DOC>   Contact Oracle support for assistance.

DOC>#######################################################################

DOC>#######################################################################

DOC>#

PL/SQL procedure successfully completed.

PL/SQL procedure successfully completed.

Commit complete.

Package altered.

Package altered.

Session altered.

SQL> 

SQL> shutdown immediate

Database closed.

Database dismounted.

ORACLE instance shut down.

SQL> 

SQL> startup

ORACLE instance started.

Total System Global Area 5368708176 bytes

Fixed Size                  8907856 bytes

Variable Size             956301312 bytes

Database Buffers         4395630592 bytes

Redo Buffers                7868416 bytes

Database mounted.

Database opened.

SQL> 

SQL> show parameter max_string_size

NAME                                 TYPE        VALUE

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

max_string_size                      string      EXTENDED

SQL> 

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