Oracle数据表默认值列添加与行迁移(Row Migration)

本文详细探讨了在 Oracle 数据库中进行数据表列添加时,尤其是带有默认值的列添加时,可能引发的 RowMigration 现象及其对系统性能的影响。通过实验演示了 RowMigration 的触发过程,并提供了如何检测及解决 RowMigration 的方法。

 

在笔者之前的文章中,已经探讨过给一个数据表添加有默认值列是一项非常“危险”的事情,特别是在在线生产环境下。给一张大数据表添加有默认值列,最直接的有下面几个严重危害:

 

ü  系统高负荷运行,消耗大量资源。添加列操作是一次性的DDL操作,生成大量的Redo Log记录;

ü  长期数据表锁定,阻碍生产系统作业。添加数据列期间,对数据表添加独占锁,此时阻碍其他DML操作;

ü  破坏原有存储结构,造成大量的行迁移(Row Migration)数据。在每个数据行尝试添加进默认值,进行膨胀的同时,由于rowid的特性,会引起严重的行链接情况,损害原有数据表存储结构;

 

本文主要想聊聊由于默认值添加带来的行链接(Row Migration)现象。

 

1、从Row Migration现象谈起

 

Row Migration本质上是一种由于Oracle存储特性和数据行定位特性而发生的一种现象。在Oracle中,所有的数据行都是保留在数据块单元上的。一个数据块可以容纳若干条数据(通常条件下)。一些数据列,如varchar2类型,大部分情况下都是根据输入数据的长度进行空间分配。

 

那么,如果数据行列填入了更大的数据,也就是空间发生了拓展。数据块存储上就会发生何种变化呢?每个数据块都会预留一部分的空闲空间,作为数据行变化预留位置。如果长度继续拓展,那么会发生什么呢?

 

Oracle会尝试将这个数据行拷贝出,找个新的数据块进行存储。这样,就可以放下数据块。那么,一个新的问题出现了,就是Rowid问题。

 

Oracle中,Rowid是定位一条记录的物理地址。Rowid包括数据文件相对编号、对象号、数据块号和Slot行号。Rowid普遍作为数据行的标记,保存在相关的索引叶子节点上。但是,当一个数据行被转移存储到另一个数据块,本质上物理存储位置已经发生变化。索引等对象中包括的Rowid面临着失效的问题。

 

Oracle解决这个问题是通过“虚拟门牌”的方法。这个数据行位置虽然已经到另外的地方,但是对应的Rowid并没有发生变化。当我们检索数据,Server Process定位到原来的位置时,它会找到一个转换跳转地址,那里面记录着真正的Rowid地址。这个就是发生了Row Migration

 

Row Migration给系统性能带来了很多潜在的问题。比如,一行数据原来只需要寻找一个数据块记录,现在就需要寻找多个数据块才可以。这样就是带来的性能问题。

 

我们在进行默认值数据行添加的时候,就会带来Row Migration的爆发。

 

2Row Migration与默认值列添加

 

下面我们通过实验,来证明Row Migration的出现。我们选择11gR2环境进行实验。

 

 

SQL> select * from v$version;

 

BANNER

--------------------------------------------------------------------------------

Oracle Database 11g Enterprise Edition Release 11.2.0.1.0 - Production

PL/SQL Release 11.2.0.1.0 - Production

CORE        11.2.0.1.0         Production

 

SQL> create table t as select object_id from dba_objects where 1=0;

Table created

 

--添加若干条记录;

SQL> insert into t select object_id from dba_objects where rownum<100;

99 rows inserted

 

SQL> commit;

Commit complete

 

 

数据表T,在存储结构和空间分配上情况如下:

 

 

SQL> exec dbms_stats.gather_table_stats(user,'T',cascade => true);

PL/SQL procedure successfully completed

 

SQL> select bytes, blocks,extents from user_segments where segment_name='T';

 

     BYTES     BLOCKS    EXTENTS

---------- ---------- ----------

     65536          8          1

 

SQL> select blocks from user_tables where table_name='T';

 

    BLOCKS

----------

         1

 

 

User_segment中记录着给数据段分配的总空间,但这并不代表全部的HWM位置。User_tables中的blocks,才代表HWM下数据块的个数。从上面的结果看,HWM下一共只有一个数据块。从rowid分析看,实际也的确如此。

 

 

SQL> select dbms_rowid.rowid_block_number(rowid) blockno, count(*) from t group by dbms_rowid.rowid_block_number(rowid);

 

   BLOCKNO   COUNT(*)

---------- ----------

     85857         99

 

 

下面我们进行数据列添加。

 

 

SQL> alter table t add vc varchar2(1000) default lpad('T',500,'T');

Table altered

 

Executed in 0.078 seconds

 

 

对应的空间使用情况如下:

 

 

SQL> exec dbms_stats.gather_table_stats(user,'T',cascade => true);

PL/SQL procedure successfully completed

 

Executed in 0.141 seconds

 

SQL> select blocks from user_tables where table_name='T';

 

    BLOCKS

----------

        12

 

Executed in 0.016 seconds

 

 

SQL> select bytes, blocks,extents from user_segments where segment_name='T';

 

     BYTES     BLOCKS    EXTENTS

---------- ---------- ----------

    131072         16          2

 

SQL> select dbms_rowid.rowid_block_number(rowid) blockno, count(*) from t group by dbms_rowid.rowid_block_number(rowid);

 

   BLOCKNO   COUNT(*)

---------- ----------

     85857         99

 

Executed in 0.016 seconds

 

 

上面的情况可以看出,Oracle的数据表T已经推高了水位线HWM12个块,从空间分配也分配了新的extent使用。

 

但是,所有数据行rowid没有变化。所有数据行的“门牌号”都没有变化,但是存储呢?很诡异的增加了。正常容量下,数据块情况应该是如下:

 

 

SQL> create table t_bak as select * from t;

 

Table created

 

SQL> exec dbms_stats.gather_table_stats(user,'T_BAK',cascade => true);

 

PL/SQL procedure successfully completed

 

SQL> select bytes, blocks,extents from user_segments where segment_name='T_BAK';

 

     BYTES     BLOCKS    EXTENTS

---------- ---------- ----------

    131072         16          2

 

SQL> select blocks from user_tables where table_name='T_BAK';

 

    BLOCKS

----------

         8

 

SQL> select dbms_rowid.rowid_block_number(rowid) blockno, count(*) from t_bak group by dbms_rowid.rowid_block_number(rowid);

 

   BLOCKNO   COUNT(*)

---------- ----------

     86589         14

     86588         14

     86585         14

     86586         14

     86591         14

     86590         14

     86587         14

     86592          1

 

8 rows selected

 

 

下面,我们来证明发生了行链接情况。

 

3、数据表行链接检验

 

Analyze语句一度是非常流行的收集数据表统计量的操作方式。但是随着dbms_stats包的成熟推广,analyze在统计量收集方面的功能已经渐渐弱化。但是,Oracle依然保留了这个语句的两个基本功能:对数据表进行行链接(Row Migration)检测和索引健康程度检测。

 

下面使用analyze语句进行数据表T的检测。首先我们需要创建分析结果的容纳数据表。

 

--调用ORACLE_HOME下的脚本;

SQL> @?/rdbms/admin/utlchain.sql

 

Table created.

 

SQL> desc chained_rows;

 Name                                      Null?    Type

 ----------------------------------------- -------- ----------------------------

 OWNER_NAME                                         VARCHAR2(30)

 TABLE_NAME                                         VARCHAR2(30)

 CLUSTER_NAME                                       VARCHAR2(30)

 PARTITION_NAME                                     VARCHAR2(30)

 SUBPARTITION_NAME                                  VARCHAR2(30)

 HEAD_ROWID                                         ROWID

 ANALYZE_TIMESTAMP                                  DATE

 

SQL> create public synonym chained_rows for chained_rows;

 

Synonym created.

 

SQL> grant all on chained_rows to public;

 

Grant succeeded.

 

 

分析数据表,如下:

 

--检验数据行Row Migration情况;

SQL> analyze table t list chained rows into chained_rows;

Table analyzed

 

Executed in 0.125 seconds

 

--发生Row Migration次数;

SQL> select count(*) from chained_rows;

 

  COUNT(*)

----------

        86

 

Executed in 0.016 seconds

 

SQL> select head_rowid from chained_rows where rownum<5;

 

HEAD_ROWID

------------------

AAASUCAABAAAU9hAAN

AAASUCAABAAAU9hAAO

AAASUCAABAAAU9hAAP

AAASUCAABAAAU9hAAQ

 

Executed in 0.016 seconds

 

SQL> select * from t where rowid='AAASUCAABAAAU9hAAQ';

 

 OBJECT_ID VC

---------- --------------------------------------------------------------------------------

        38 TTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTT

 

Executed in 0.016 seconds

 

 

99行记录中,发生了86次行链接Row Migration情况。

 

 

4、结论

 

解决Oracle Row Migration的方法,就是进行数据表重构,重新对存储结构和Rowid进行整理。我们说,在生产环境下,进行有默认值数据列的添加操作,会引起一系列的问题,要三思而行。

 

来自 “ ITPUB博客 ” ,链接:http://blog.itpub.net/17203031/viewspace-742101/,如需转载,请注明出处,否则将追究法律责任。

转载于:http://blog.itpub.net/17203031/viewspace-742101/

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值