0

0

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

php中文网

php中文网

发布时间:2016-06-07 17:58:05

|

1425人浏览过

|

来源于php中文网

原创

在笔者之前的文章中,已经探讨过给一个数据表添加有默认值列是一项非常危险的事情,特别是在在线生产环境下。给一张大数据表添加有默认值列,最直接的有下面几个严重危害: 系统高负荷运行,消耗大量资源。添加列操作是一次性的DDL操作,生成大量的Redo Log记

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

系统高负荷运行,消耗大量资源。添加列操作是一次性的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的爆发。

2、Row 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
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已经推高了水位线HWM到12个块,从空间分配也分配了新的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

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进行整理。我们说,在生产环境下,进行有默认值数据列的添加操作,会引起一系列的问题,要三思而行。

热门AI工具

更多
DeepSeek
DeepSeek

幻方量化公司旗下的开源大模型平台

豆包大模型
豆包大模型

字节跳动自主研发的一系列大型语言模型

通义千问
通义千问

阿里巴巴推出的全能AI助手

腾讯元宝
腾讯元宝

腾讯混元平台推出的AI助手

文心一言
文心一言

文心一言是百度开发的AI聊天机器人,通过对话可以生成各种形式的内容。

讯飞写作
讯飞写作

基于讯飞星火大模型的AI写作工具,可以快速生成新闻稿件、品宣文案、工作总结、心得体会等各种文文稿

即梦AI
即梦AI

一站式AI创作平台,免费AI图片和视频生成。

ChatGPT
ChatGPT

最最强大的AI聊天机器人程序,ChatGPT不单是聊天机器人,还能进行撰写邮件、视频脚本、文案、翻译、代码等任务。

相关专题

更多
pixiv网页版官网登录与阅读指南_pixiv官网直达入口与在线访问方法
pixiv网页版官网登录与阅读指南_pixiv官网直达入口与在线访问方法

本专题系统整理pixiv网页版官网入口及登录访问方式,涵盖官网登录页面直达路径、在线阅读入口及快速进入方法说明,帮助用户高效找到pixiv官方网站,实现便捷、安全的网页端浏览与账号登录体验。

463

2026.02.13

微博网页版主页入口与登录指南_官方网页端快速访问方法
微博网页版主页入口与登录指南_官方网页端快速访问方法

本专题系统整理微博网页版官方入口及网页端登录方式,涵盖首页直达地址、账号登录流程与常见访问问题说明,帮助用户快速找到微博官网主页,实现便捷、安全的网页端登录与内容浏览体验。

135

2026.02.13

Flutter跨平台开发与状态管理实战
Flutter跨平台开发与状态管理实战

本专题围绕Flutter框架展开,系统讲解跨平台UI构建原理与状态管理方案。内容涵盖Widget生命周期、路由管理、Provider与Bloc状态管理模式、网络请求封装及性能优化技巧。通过实战项目演示,帮助开发者构建流畅、可维护的跨平台移动应用。

64

2026.02.13

TypeScript工程化开发与Vite构建优化实践
TypeScript工程化开发与Vite构建优化实践

本专题面向前端开发者,深入讲解 TypeScript 类型系统与大型项目结构设计方法,并结合 Vite 构建工具优化前端工程化流程。内容包括模块化设计、类型声明管理、代码分割、热更新原理以及构建性能调优。通过完整项目示例,帮助开发者提升代码可维护性与开发效率。

20

2026.02.13

Redis高可用架构与分布式缓存实战
Redis高可用架构与分布式缓存实战

本专题围绕 Redis 在高并发系统中的应用展开,系统讲解主从复制、哨兵机制、Cluster 集群模式及数据分片原理。内容涵盖缓存穿透与雪崩解决方案、分布式锁实现、热点数据优化及持久化策略。通过真实业务场景演示,帮助开发者构建高可用、可扩展的分布式缓存系统。

26

2026.02.13

c语言 数据类型
c语言 数据类型

本专题整合了c语言数据类型相关内容,阅读专题下面的文章了解更多详细内容。

29

2026.02.12

雨课堂网页版登录入口与使用指南_官方在线教学平台访问方法
雨课堂网页版登录入口与使用指南_官方在线教学平台访问方法

本专题系统整理雨课堂网页版官方入口及在线登录方式,涵盖账号登录流程、官方直连入口及平台访问方法说明,帮助师生用户快速进入雨课堂在线教学平台,实现便捷、高效的课程学习与教学管理体验。

14

2026.02.12

豆包AI网页版入口与智能创作指南_官方在线写作与图片生成使用方法
豆包AI网页版入口与智能创作指南_官方在线写作与图片生成使用方法

本专题汇总豆包AI官方网页版入口及在线使用方式,涵盖智能写作工具、图片生成体验入口和官网登录方法,帮助用户快速直达豆包AI平台,高效完成文本创作与AI生图任务,实现便捷智能创作体验。

524

2026.02.12

PostgreSQL性能优化与索引调优实战
PostgreSQL性能优化与索引调优实战

本专题面向后端开发与数据库工程师,深入讲解 PostgreSQL 查询优化原理与索引机制。内容包括执行计划分析、常见索引类型对比、慢查询优化策略、事务隔离级别以及高并发场景下的性能调优技巧。通过实战案例解析,帮助开发者提升数据库响应速度与系统稳定性。

53

2026.02.12

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
SQL 教程
SQL 教程

共61课时 | 3.9万人学习

Java 教程
Java 教程

共578课时 | 67.4万人学习

oracle知识库
oracle知识库

共0课时 | 0.6万人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号