Lob字段包含三个部分:Lob列、lob数据段、lob索引段。
Lob列由lob定位符也就是指向lob字段实际存放位置的指针和lob值组成。
Lob数据段可以分为多个chunk,每个chunk用来存放lob数据。
Lob索引段存放lob索引,由多个entry组成,每个entry结构大致为lob index locator、chunk id和chunk location,一个entry指向一个lob块。也就是记录在lob索引段里面的entry等于lob数据段内的chunk数目。
结构大体如下:
Lob列 LOB定位符 lob值
Lob数据段 chunk1 chunk2 chunk3 chunk4…
Log 索引段 entry entry entry entry…
SQL> create table tab_lob01(id number,cnt clob)
2 tablespace users
3 lob(cnt) store as seg_cnt01(tablespace netbaisdata
4 chunk 32K
5 pctversion 5
6 );
Table created
其中会创建一个表段,lob数据段和lob索引段,其中制定的seg_cnt01的lob数据段存储在netbaisdata段内,而系统预定义的lob索引段也会存储在netbaisdata表空间中。
其中的上述的chunk也就是分配给lob段的最小单元,由于lob字段的读取和写入都是以chunk为单位,一般chunk为db_block_size的整数倍,oracle10g 版本最大值可以为2G,默认情况使用最小值。
Enable/disable storage in row
Enable storage in row模式表示小于4K的lob字段存储在表段内,大于4k的lob字段存储在lob数据段内。
Disable storage in row不管lob字段的长度都存储在lob数据段内,仅仅只是在表的行内存储lob定位符。
Pctversion
Lob数据段不适用回滚机制,即当更新lob数据时,lob数据段内分配新chunk去插入一条新的记录,不对前映像修改,那么如果lob字段更新较多,肯定会产生很多的新version,会浪费较多的空间,所以需要用pctversion来控制前映像空间在lob数据空间中所占用的比例。大于此比例将会重用这些前映像,默认值是10%,最小设置为0,最大100%。
Cache/nocache/cache read
Cache表示将lob数据段放在缓存中,nocache不放在缓存中,cache read则是读的时候会把lob存储在缓存中,默认的是nocache。
Loc的存储位置:
根据lob字段的存储中的enable| disbale storage in row分析,当采用enable storage in row时,如果lob字段小于4K,也就是in-line blob的会直接存储在表段内;而当大于4K时(out-of-line)且lob字段没有超过12个的chunk,lob定位符直接指向lob数据段内的数据,不用lob索引段。Disable storage in row或者是二进制数据大于4k,而且超过12chunk时,需要通过lob定位符—lob索引段—lob数据段,也就是每一个lob数据段内的每个chunk地址都存在lob索引中。
SQL> select avg(dbms_lob.getlength(in_flow_img)) from ql_inf;
AVG(DBMS_LOB.GETLENGTH(IN_FLOW
------------------------------
93100.3484848485
SQL> select segment_name,chunk,pctversion,cache,logging,in_row from user_lobs where column_name='IN_FLOW_IMG' and table_name='QL_INF';
SEGMENT_NAME CHUNK PCTVERSION CACHE LOGGING IN_ROW
------------------------------ ---------- ---------- ---------- ------- ------
SYS_LOB0000052854C00019$$ 8192 10 NO YES YES
Lob字段in_flow_img的SYS_LOB0000052854C00019$$的默认chunk是8kb,而实际我们存储的字段大概有91kb的样子,由于一个chunk只存储一行数据,实际会占用12个chunk,那么会产生过多的行连接,这里设置较大的chunk会更好,不够在缺省的db_block_size 8kb下只能设置32k的chunk,超过这个数在oracle10.2R下就会出现invalid lob option value的ora-22851错误。当然如果实际的lob字段容量较小,比如4kb,而实际你的chunk默认的是8kb,由于chunk只存储一行数据,同样会占用8kb的大小,其余的空间也不会节省出来。
SQL> create table lob01(cnt1 clob,cnt2 clob) lob(cnt2) store as(disable storage in row);
Table created
SQL> insert into lob01 values(lpad('x',10000,'x'),rpad('x',10000,'x'))
;
2 /
1 row inserted
SQL> commit;
Commit complete
SQL> select avg(dbms_lob.getlength(cnt1)),avg(dbms_lob.getlength(cnt2)) from lob01;
AVG(DBMS_LOB.GETLENGTH(CNT1)) AVG(DBMS_LOB.GETLENGTH(CNT2))
----------------------------- -----------------------------
4000 4000
可以看出都应该在同一个chunk内,默认chunk是8kb。那么disable和enable所走的IO将不同。
SQL> set autotrace traceonly statistics
SQL> select cnt1 from lob01;
统计信息
----------------------------------------------------------
0 recursive calls
0 db block gets
11 consistent gets
4 physical reads
0 redo size
1023 bytes sent via SQL*Net to client
678 bytes received via SQL*Net from client
5 SQL*Net roundtrips to/from client
0 sorts (memory)
0 sorts (disk)
1 rows processed
SQL> select cnt2 from lob01;
统计信息
----------------------------------------------------------
0 recursive calls
0 db block gets
19 consistent gets
4 physical reads
0 redo size
963 bytes sent via SQL*Net to client
638 bytes received via SQL*Net from client
5 SQL*Net roundtrips to/from client
0 sorts (memory)
0 sorts (disk)
1 rows processed
多执行几次减小物理IO,可以看出真正的physical reads并不能绝对的减小到0,那是因为lob默认的是不会cache的。
SQL> alter table lob01 move tablespace netbaisdata
2 lob(cnt1) store as(cache)
3 lob(cnt2) store as(cache);
表已更改。
SQL> select cnt1 from lob01;
统计信息
----------------------------------------------------------
62 recursive calls
0 db block gets
23 consistent gets
3 physical reads
72 redo size
1023 bytes sent via SQL*Net to client
678 bytes received via SQL*Net from client
5 SQL*Net roundtrips to/from client
0 sorts (memory)
0 sorts (disk)
1 rows processed
SQL> select cnt1 from lob01;
统计信息
----------------------------------------------------------
0 recursive calls
0 db block gets
7 consistent gets
0 physical reads
0 redo size
1023 bytes sent via SQL*Net to client
678 bytes received via SQL*Net from client
5 SQL*Net roundtrips to/from client
0 sorts (memory)
0 sorts (disk)
1 rows processed
SQL> select cnt2 from lob01;
统计信息
----------------------------------------------------------
1 recursive calls
0 db block gets
15 consistent gets
0 physical reads
0 redo size
963 bytes sent via SQL*Net to client
638 bytes received via SQL*Net from client
5 SQL*Net roundtrips to/from client
0 sorts (memory)
0 sorts (disk)
1 rows processed
可以看出设置cache后,物理读会慢慢减少到0,全部转化为逻辑读,但是由于cnt2是disable storage in row的,需要先根据lob定位器然后到lob索引段最后到lob数据段,需要访问更多的IO,所有逻辑读会大于cnt1.
接着看下面的事例:
SQL> select count(rowid) from jhqlnwhxdb.ql_inf;
COUNT(ROWID)
------------
16810
SQL> select count(rowid) from jhqlnwhxdb.ql_inf where dbms_lob.getlength(in_flow_img)>4096;
COUNT(ROWID)
------------
16803
SQL> select avg(dbms_lob.getlength(in_flow_img)) from jhqlnwhxdb.ql_inf where dbms_lob.getlength(in_flow_img)>4096;
AVG(DBMS_LOB.GETLENGTH(IN_FLOW
------------------------------
268087.183717193
可以看出in_flow_img的基本都大于4k,其大于4k的数据平均长度要达到160k的样子,如果大部分lob字段小于4k,则肯定是enable storage in row模式优先,有效的减少了行链接,而如果大于4k但是还是没有超过12个chunk,其实也就是数据的长度没有达到缺省的12*8kb也就是96kb的值,也可以选择enable模式。如果大部分大于4k且基本超过了12 chunk值,也就是out-of-line存储的比例较高,应该选择disable模式,这样lob数据会存储在外面的lob数据段,如果进行fts或者查询非lob字段,将大大加快存储访问的速度,这个是需要dba更多的关注的,而另外也可以设置较大的chunk,oracle每次的IO数据就会多一些,总体会减少相应的IO。
Lob数据段和lob索引段可以分开存储到不同的tbs下,然后到不同的磁盘,减小磁盘的竞争,对于lob数据而言,只要一行数据超过oracle数据块的大小必产生行连接,我们可以通过analyze后来分析表中的行连接,通过oracle concept中就清楚设计中要相应的减少了行链接和行迁移,因为一旦过多的IO越来越多就可能往往成为你系统瓶颈的最终原因,不过生产中暂时还没有碰见这个成为瓶颈的案例。
SQL> analyze table jhqlnwhxdb.ql_inf compute statistics;
Table analyzed
SQL> select chain_cnt from dba_tables where table_Name='QL_INF' and
2 owner='JHQLNWHXDB';
CHAIN_CNT
----------
220
[@more@]来自 “ ITPUB博客 ” ,链接:http://blog.itpub.net/25362835/viewspace-1058577/,如需转载,请注明出处,否则将追究法律责任。
转载于:http://blog.itpub.net/25362835/viewspace-1058577/