1.如果一行的数据太大以至一个单独的block容纳不下,会产生两种现象:
1)
A、Chaining:插入新数据时,行数据太大以至一个空block容纳不下,oracle会将这一行的数据存放在一个或多个block 组成的block chain中,insert、update都可能导致这个问题,在某些情况下row chaining是不能避免的。
B、Migration:修改数据时,可能导致行数据增大,以至它所在的block剩余空间容纳不下,oracle server会去寻找一个有足够自由空间容纳整行数据的block,如果这样的block存在,oracle server把整行移到新的block,在原位置保存一个指向新存放位置的镜像行,镜像行的rowid和原来的rowid一致。
其实行迁移是行链接的一种特殊形式,但是它的起因与行为跟行链接有很大不同,所以一般把它从行链接中独立出来,单独进行处理。
2) 行链接和行迁移引起数据库性能下降的原因:
引起性能下降的原因主要是由于引起多余的I/O造成的。当通过索引访问已有行迁移现象的行时,数据库必须扫描一个以上的数据块才能检索到改行的数据。这主要有一下两种表现形式:
A 、导致row migration 或row chaining INSERT 或 UPDATE语句的性能较差,因为它们需要执行额外的处理
B 、利用索引查询已经链接或迁移的行的select语句性能比较差,因为它们要执行额外的I/O
2.测试和检测migration和chaining:
1)测试
create table test ( x int primary key, a char(2000), b char(2000), c char(2000), d char(2000), e char(2000) ) tablespace TS_TEST;
查看TS_TEST这个表空间的数据文件号
select file_id from dba_data_files where tablespace_name='TS_TEST';
数据库的db_block_size是8K,创建的表有五个字段,每个占2000个字节,这样一行记录大约10K,就能超过一个block的大小了。
insert into test(x) values (1);COMMIT;
查找这行记录所在的block,并dump出来
select dbms_rowid.rowid_block_number(rowid) from test;
alter system dump datafile 23 block 34;
在udump目录下查看trace文件的内容
update test set a='test',COMMIT;
alter system dump datafile 23 block 34;
trace文件的内容:nrid出现了值,指向了下一个row id,证明刚刚的update操作使这行记录产生了行链接或者行迁移了
2)检测
A、系统级别
SELECT name, value FROM v$sysstat WHERE name = 'table fetch continued row';
B、表级别
Analyize table table_name compute statistics;
Select num_rows,chain_cnt from dba_tables where table_name=’...’;
C、记录级别
@?/rdbms/admin/utlchain.sql
ANALYZE TABLE table_name LIST CHAINED ROWS;
select owner_name,table_name,head_rowid from chained_rows
3.解决方法:
防范于未然:
1)行链接
在大多数情况下,行链接是无法克服的,特别是在一个表包含象LONGS, LOBs 等这样的列时。当在不同的表中有大量的链接行,并且哪些表的行的长度不是很长时,你可以通过用更大的block size重建数据库的方法来解决它或者修改表的结构。
例如:当前你的数据库的数据块的大小为4K,但是你的行的平均长度为6k,那么你可以通过用8k大小的数据块来重建数据库的办法解决行链接现象。
2)行迁移
产生Migration的原因可能是由于PCTFREE设置的太低以至没有保留足够的空间用于更新,可以通过增加PCTFREE的值避免行镜像产生。但这种解决办法是以牺牲更多的空间为代价的,这也就是我们通常所说的以空间换效率。而且通过增加PCTFREE值的办法只能缓解行迁移现象,而不能完全解决行迁移,所以较好的办法是在设置了合适的PCTFREE值的后,在发现行迁移现象比较严重时,对表的数据进行重组。
亡羊补牢:
3)表重组
若某张表迁移现象严重,可考虑对表进行重组,即使用export+truncate/drop原表+import或者把表移入另一个表空间(alrer table table_name move tablespace_name);注意移动表时,要考虑表上的约束和索引等。
4)记录重组
运行analyize table ... list chained rows;
复制镜像行到另一个表tmp;
从源表中删除这些行;
从tmp中将这些行插回到源表中。
脚本:
/* 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
set echo on
spool fix_mig
/* List the chained & 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
使用这个脚本时,必须将涉及到的外键约束去掉。