已归录

行链接与行迁移的定义

行链接:当 insert 的时候,如果一行数据大到在一个 block 中放不下,Oracle 就会把该行数据放到多个块中,这些块链起来。行链接经常发生在行中含有大数据类型,例如 LONG 或者 LONG RAW。在这些情况下行链接是不可避免的。

行迁移:当 update 的时候,行变长导致当前 block 容纳不下(不一定只是一行变长,也可能是多行都变长了),于是将整行数据迁移至一个新的 block 中,原 block 中保留指向新 block 的一个指针。原始块中的指针是必需的,因为 The ROWID of a migrated row does not change.

不论是行链接还是行迁移,该行的 IO 性能都会下降,因为 Oracle 需要扫描多个块才能获取到该行信息。

问题发现

分析表:

SQL> analyze table hr.test compute statistics;

考点:对于大部分目的而言,应该用 DBMS_STATS 包中的过程分析表,但要查看行链接或行迁移信息,只能通过 ANALYZE 命令检测。

user_tables 的 CHAIN_CNT 列表明了该表行链接或者行迁移的数量。尽管行迁移与行链接是两个不同的事情,但是在 Oracle 内部,它们被当作一回事。所以当你检测到该列有值时,你应该仔细的分析当前你正在处理的是行迁移还是行链接或者两者都有。当 CHAIN_CNT 有值时,看 AVG_ROW_LEN 列,它表示行的平均长度(byte),如果 "AVG_ROW_LEN < 块大小",那么是迁移行,如果"AVG_ROW_LEN > 块大小",那么是链接行。

解决思路

在大多数情况下,行链接是无法克服的,特别是在一个表包含大对象 LONGS、LOBs 等这样的列时。当在不同的表中有大量的链接行,并且那些表的行的长度不是很长时,你可以通过用更大的 block size 重建数据库(表空间)的方法来解决它。例如:当前你的数据库(表空间)的数据块的大小为 4K,但是你的行的平均长度为 6k,那么你可以通过用 8k 大小的数据块来重建数据库(表空间)的办法解决行链接现象。

行迁移主要是由于设置的 PCTFREE 参数过小,导致没有给 update 操作留下足够的空闲空间引起。为了避免行迁移,表在创建时应该设置合适 PCTFREE 值,以便在每个数据块内为数据修改保留足够的空间。可以通过增加 PCTFREE 值的办法来避免行迁移,但这种解决办法是以牺牲更多的空间为代价的,这也就是我们通常所说的以空间换效率。而且通过增加 PCTFREE 值的办法只能缓解行迁移现象,而不能完全解决行迁移,所以较好的办法是在设置了合适的 PCTFREE 值的后,在发现行迁移现象比较严重时,对表的数据进行重组。

解决方法

总体来说,我们有两种处理办法:一是 move 表,一是 CTAS 来重组数据(找出迁移的行,delete 再 insert)。

方法一:move 表

我们可以通过 move 表来解决行链接和行迁移,如果是行链接,则把表 move 到更大块的表空间,如果是行迁移,可以在原表空间 move 表,也可以把表 move 到其它表空间。
如果对表做了 move 操作,如注意如下问题:

1、索引将 unsuable
2、如果没有重建索引就对表做 analyze table xxx compute statistics; 将报错
2、统计数据将失效
3、可降低 HWM
Moving a table changes the rowids of the rows in the table. This causes indexes on the table to be marked UNUSABLE, and DML accessing the table using these indexes will receive an ORA-01502 error. The indexes on the table must be dropped or rebuilt. Likewise, any statistics for the table become invalid and new statistics should be collected after moving the table. 另外, move 表可以降低 HWM。

方法二:CTAS(重建记录)

写一个如下脚本,运行脚本的时候输入产生行迁移的表的表名即可:

$ vim chain.sql

-- 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
-- utlchain.sql 脚本的作用仅仅是创建一个 chained_rows 空表。
@?/rdbms/admin/utlchain.sql
set echo on
spool fix_mig
-- List the chained and migrated rows
-- 下面的命令会把迁移行或者链接行找出来并插入到上面创建的 chained_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
-- By 许望(RHCA、OCM、VCP)
最后修改:2020 年 05 月 22 日 02 : 53 PM
如果觉得我的文章对你有用,请随意赞赏