攻略1-12 重建跨多个数据块的数据行

问题描述

你像下面这样运行生成了一份段顾问报告:

enter image description here

并注意到输出表明表中出现了行链接:

enter image description here

你认识到行迁移/行链接将会产生较高的I/O率,从而导致较差的性能。因此,需要消除这张表上的行迁移/行链接。

解决方案

当一个数据块没有足够的空间来容纳一行数据的时候,就会产生行迁移/行链接,因此Oracle使用多个数据块来存储一行数据(关于这一点的更多细节在本攻略的“工作原理”部分加以阐述)。行迁移/行链接的问题在于,它们可能会导致读取数据行时产生过多的I/O。有三种基本的技术可以用来解决行迁移/行链接问题:

 移动表 
 单独移动表中产生行迁移/行链接的数据行 
 使用数据泵(导出/导入)重建表 
其中前两种方法是本节“解决方案”部分重点论述的内容。数据泵技术包括导出表,然后删除或重命名现有的表,再将表从导出的文件中重新导入。关于如何使用数据泵的更多细节,可以参考Pro Oracle Database 12c Administration或Oracle技术网站上的Oracle’s Utility Guide。

移动表

解决行迁移/行链接问题的一个办法就是使用MOVE语句,并在具有更低的PCTFREE值的情况下重建表和数据行。这里的想法是,当具有更低的PCTFREE值时,就会为数据块中迁移或链接的数据行留下更多的空间(因为从具有较高PCTFREE值的数据块移动到了PCTFREE值较低、从而有更大空间的数据块中)。

例如,假设开始建表时PCTFREE值设置为40%。接下来的移动运算重建表时PCTFREE值为5%:

enter image description here

但是,要记住如果这样做,可能会使问题变得更糟糕,因为数据块中留给未来更新用的空间更少了(这将引发更多的行迁移/行链接)。

注意 当移动表的时候,Oracle需要表上的排它锁。因此,需要在所移动的表上没有活动事务的时候来进行这个运算。

同时,作为移动操作的一部分,所有数据行都会被分配一个新的行编号(ROWID)。这会使表的所有索引失效。因此作为移动操作的一部分,你需要重建所有与进行移动的表相关的索引。可以通过查询DBA/ALL/USER_INDEXES视图来验证索引的状态:

enter image description here

通过这种方法来识别行迁移/行链接数据行的好处是,可以通过做下面这些事情,在不影响表中其他数据的情况下,解决行迁移/行链接问题:

(1) 创建一张临时表来存储存在行链接的数据行;

(2) 从原始表中将存在行迁移/行链接的数据行删除;

(3) 将临时表中的数据插入到初始表中。

因为这种方法具有多个步骤,我们建议尝试在生产环境数据库中实施之前,首先在非生产环境中进行测试。下面是说明上述步骤的一个简单例子。首先创建一张临时表,来容纳在CHAINED_ROWS表中具有对应记录的EMP表中数据行:

enter image description here

上面的移动行迁移/行链接数据行的步骤应该会清除所有的行迁移问题。现在可以删除或截断CHAINED_ROWS表中的记录,并删除临时表。

如何知道行迁移或行链接问题是不是解决了呢?重复ANALYZE TABLE ... LIST CHAINED ROWS的步骤。如果在CHAINED_ROWS表中又创建了新的数据行,则很可能仍然具有行链接(但不是行迁移)的问题。要解决这个问题的话,就只能移动表,并将PCTFREE设置为更小的值,或者将表移动到尺寸更大的数据块中去(更多细节参见“工作原理”部分)。

理解Oracle的ROWID

每张表中的每一行数据都具有一个物理地址。一个数据行的地址由下面这些因素共同 确定:

q 数据文件编号;

q 数据块编号;

q 数据行在数据块中的位置;

q 对象编号。

可以查询ROWID伪列来显示一个数据行在表中的地址,例如:

enter image description here

可以在SQL语句的SELECT和WHERE子句中使用ROWID值。在大多数情况下,ROWID唯一标识一个数据行。但是,也有可能不同表中的数据行存储在同一个簇中,从而具有同样的ROWID(就像是在群集表中那样)。

工作原理

Oracle将行链接定义为,一个数据行太大,无法存放在一个数据块的空闲空间中,因此需要两个或多个数据块来存储该数据行。当存在下面这些情况时,会发生行链接。

 初始插入一行数据的时候,其中各列的值导致这一行数据太长,在任何可用的数据块中都无法存储。对于空白数据块,这个大小大约是数据块的大小减去PCTFREE保留的部分。 
 初始插入一行数据的时候,是可以存储到一个数据块中的,但当更新之后,数据行就变长了,无法存储在一个数据块的空闲空间中(PCTFREE设置决定了数据块空间中有多少百分比是预留给更新用的)。 
 一张超过255列数据的表需要两个或更多的数据块来保存。 
Oracle将会在包含行链接数据行的数据块之间维护指针。这意味着任何时候读取这些数据行,都需要有多次的I/O运算。同时更新和删除也需要写入到多个数据块中。如果存在大量的行链接,就可能会影响性能。尽管如此,如果一个数据行确实太大,无法存储在任何可用的数据块中,要解决这个问题的办法要么就是减小数据行的大小,要么就是增大数据块容量。

对于具有255列或更少列的表,或许可以通过移动表,同时降低PCTFREE的值来解决行链接问题。如果这样不能解决问题,则可以创建一个具有更大数据块容量的表空间,并将表移动到该表空间下。除了这些办法之外,对于大数据行是无法避免行链接的。

Oracle将行迁移定义为,在初始插入数据行到数据块中的时候,该值足够小,可以存放在数据块的空闲空间中。但数据行中包含的列在之后更新为更大的值(例如一列初始插入空值,后来更新为非空的值),导致数据行无法存储在当前的数据块中。但是,该数据行仍然足够小,可以存储在其他可用的数据块的空闲空间中。在这种情况下,Oracle将会将这一行移动(迁移)到具有足够空间的数据块中。

对于行迁移,Oracle在初始数据块中维护一个指针,指向数据行迁移后的数据块。因为每次读取数据行的时候,Oracle都需要读/写多个数据行,所以行迁移会影响性能。行迁移几乎都可以通过本攻略“解决方案”部分所给出的移动表的方法来解决。

目录