当一行的数据过长而不能插入一个单个数据块中时,可能发生两种事情:行链接(row chaining)或行迁移(row migration)。
行迁移: 当一个行上的更新操作(原来的数据存在且没有减少)导致当前的数据不能在容纳在当前块,我们需要进行行迁移。一个行迁移意味着整行数据将会移动,仅仅保留的是一个转移地址。因此整行数据都被 移动,原始的数据块上仅仅保留的是指向新块的一个地址信息。
产生:update
行链接: 当一行数据太大而不能在一个单数据块容纳时,行链接由此产生。举例来说,当你使用了4kb的Oracle 数据块大小,而你需要插入一行数据是8k,Oracle则需要使用3个数据块分成片来存储。因此,引 起行链接的情形通常是,表上行记录的大小超出了数据库Oracle块的大小。
产生:insert
表上使用了LONG 或 LONG RAW数据类型的时候容易产生行链接。其次表上多于255列时Oracle会将这些过宽的表分片而产生行链接
迁移行对索引读产生额外的I/O,对全表扫描没什么影响
行链接则影响索引读和全表扫描行额外的处理
2) 利用索引查询已经链接或迁移的行的select语句性能比较差,因为它们要执行额外的I/O
http://www.itpub.net/thread-1618576-1-1.html
行迁移主要是由于设置的PCTFREE参数过小,导致没有给update操作留下足够的空闲空间引起。为了避免行迁移,所有被修改的表应该设置合适的PCTFREE 值,以便在每个数据块内为数据修改保留足够的空间。可以通过增加PCTFREE值的办法来避免行迁移,但这种解决办法是以牺牲更多的空间为代价的,这也就是我们通常所说的以空间换效率。 而且通过增加PCTFREE值的办法只能缓解行迁移现象,而不能完全解决行迁移,所以较好的办法是在设置了合适的PCTFREE值的后,在发现行迁移现象比较严重时,对表的数据进行重组。下面是对行迁移数据进行重组的步骤(这种方法也被成为CTAS):
PS:
PCTFREE: 数据块的可用空间低于多少的时候,不能再insert,只能update
PCTUSED: 数据块的数据低于多少的时候,可以再insert。
http://www.cnblogs.com/linjiqin/archive/2012/01/16/2323320.html
二、如何检测出来行迁移或者行链接:
1.创建chained rows需要的表:
sql> @/home/oracle/product/10.xx/rdbms/admin/utlchain.sql
2.将表中的chained row移动到chained_rows表
SQL> analyze table test_table_name list chained rows;
SQL> SELECT * FROM chained_rows;
或者:
当然你也可以通过检查v$sysstat视图中的'table fetch continued row'来检查被迁移或被链接的行。
SQL> SELECT name, value FROM v$sysstat WHERE name = 'table fetch continued row';
NAME VALUE
---------------------------------------------------------------- ---------
table fetch continued row 308
三、解决方法
analyze table 之后:
select table_name,num_rows,CHAIN_CNT from user_tables ;
or
select table_name,num_rows,CHAIN_CNT from dba_tables where owner='XXXXXXX' ;
方法1:直接truncate表,然后再导入导出 datapump,但这样downtime比较长,而且如果有外键约束之类的,比较麻烦。
方法2:alter table move,然后rebuild index
analyze table t compute statistics
举例:
SQL> alter table t add t1 date default sysdate;
Table altered.
SQL> c/t1/t2
1* alter table t add t2 date default sysdate
SQL> /
Table altered.
SQL> c/t2/t3
1* alter table t add t3 date default sysdate
SQL> /
Table altered.
SQL> analyze table t compute statistics;
Table analyzed.
SQL> select table_name,num_rows,CHAIN_CNT from user_tables where table_name='T';
TABLE_NAME NUM_ROWS CHAIN_CNT
------------------------------ ---------- ----------
T 41616 3908
SQL> alter table t move ;
Table altered.
SQL> analyze table t compute statistics;
Table analyzed.
SQL>select table_name,num_rows,CHAIN_CNT from user_tables where table_name='T';
TABLE_NAME NUM_ROWS CHAIN_CNT
------------------------------ ---------- ----------
T 41616 0
rebuild index
方法3: 通过数据字典来chained_ROWS,然后analyze table XX LIST chained ROWS;创建中间表,查出并迁移行。
参考http://blog.csdn.net/hotye393/article/details/6226471
-- Get the name of the table with migrated rows:
ACCEPT table_name PROMPT 'Enter the name of the table with migrated rows: '
-- Clean up from last execution
set echo off
DROP TABLE migrated_rows;
DROP TABLE chained_rows;
-- Create the CHAINED_ROWS table
@.../rdbms/admin/utlchain.sql
set echo on
spool fix_mig
-- List the chained and migrated rows
ANALYZE TABLE &table_name LIST CHAINED ROWS;
-- Copy the chained/migrated rows to another table
create table migrated_rows as
SELECT orig.*
FROM &table_name orig, chained_rows cr
WHERE orig.rowid = cr.head_rowid
AND cr.table_name = upper('&table_name');
-- Delete the chained/migrated rows from the original table
DELETE FROM &table_name WHERE rowid IN (SELECT head_rowid FROM chained_rows);
-- Copy the chained/migrated rows back into the original table
INSERT INTO &table_name SELECT * FROM migrated_rows;
spool off
但是如果chained_rows本身就存在链接行,那么只能改变block 的大小来解决了。
四、数据块大小的选择标准:
小的:
行的宽度很小,并伴有大量的随机访问(Random Access)
减少块竞争
注:选择小尺寸数据块会浪费大量的存储空间,
大的:
行的宽度很大,或者含有LOB字段
应用中存在大量的顺序读(Sequential Access)
节省I/O,一次可以读入更多的数据量
节省存储空间