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不单是聊天机器人,还能进行撰写邮件、视频脚本、文案、翻译、代码等任务。

相关专题

更多
Go高并发任务调度与Goroutine池化实践
Go高并发任务调度与Goroutine池化实践

本专题围绕 Go 语言在高并发任务处理场景中的实践展开,系统讲解 Goroutine 调度模型、Channel 通信机制以及并发控制策略。内容包括任务队列设计、Goroutine 池化管理、资源限制控制以及并发任务的性能优化方法。通过实际案例演示,帮助开发者构建稳定高效的 Go 并发任务处理系统,提高系统在高负载环境下的处理能力与稳定性。

2

2026.03.10

Kotlin Android模块化架构与组件化开发实践
Kotlin Android模块化架构与组件化开发实践

本专题围绕 Kotlin 在 Android 应用开发中的架构实践展开,重点讲解模块化设计与组件化开发的实现思路。内容包括项目模块拆分策略、公共组件封装、依赖管理优化、路由通信机制以及大型项目的工程化管理方法。通过真实项目案例分析,帮助开发者构建结构清晰、易扩展且维护成本低的 Android 应用架构体系,提升团队协作效率与项目迭代速度。

24

2026.03.09

JavaScript浏览器渲染机制与前端性能优化实践
JavaScript浏览器渲染机制与前端性能优化实践

本专题围绕 JavaScript 在浏览器中的执行与渲染机制展开,系统讲解 DOM 构建、CSSOM 解析、重排与重绘原理,以及关键渲染路径优化方法。内容涵盖事件循环机制、异步任务调度、资源加载优化、代码拆分与懒加载等性能优化策略。通过真实前端项目案例,帮助开发者理解浏览器底层工作原理,并掌握提升网页加载速度与交互体验的实用技巧。

80

2026.03.06

Rust内存安全机制与所有权模型深度实践
Rust内存安全机制与所有权模型深度实践

本专题围绕 Rust 语言核心特性展开,深入讲解所有权机制、借用规则、生命周期管理以及智能指针等关键概念。通过系统级开发案例,分析内存安全保障原理与零成本抽象优势,并结合并发场景讲解 Send 与 Sync 特性实现机制。帮助开发者真正理解 Rust 的设计哲学,掌握在高性能与安全性并重场景中的工程实践能力。

187

2026.03.05

PHP高性能API设计与Laravel服务架构实践
PHP高性能API设计与Laravel服务架构实践

本专题围绕 PHP 在现代 Web 后端开发中的高性能实践展开,重点讲解基于 Laravel 框架构建可扩展 API 服务的核心方法。内容涵盖路由与中间件机制、服务容器与依赖注入、接口版本管理、缓存策略设计以及队列异步处理方案。同时结合高并发场景,深入分析性能瓶颈定位与优化思路,帮助开发者构建稳定、高效、易维护的 PHP 后端服务体系。

339

2026.03.04

AI安装教程大全
AI安装教程大全

2026最全AI工具安装教程专题:包含各版本AI绘图、AI视频、智能办公软件的本地化部署手册。全篇零基础友好,附带最新模型下载地址、一键安装脚本及常见报错修复方案。每日更新,收藏这一篇就够了,让AI安装不再报错!

116

2026.03.04

Swift iOS架构设计与MVVM模式实战
Swift iOS架构设计与MVVM模式实战

本专题聚焦 Swift 在 iOS 应用架构设计中的实践,系统讲解 MVVM 模式的核心思想、数据绑定机制、模块拆分策略以及组件化开发方法。内容涵盖网络层封装、状态管理、依赖注入与性能优化技巧。通过完整项目案例,帮助开发者构建结构清晰、可维护性强的 iOS 应用架构体系。

180

2026.03.03

C++高性能网络编程与Reactor模型实践
C++高性能网络编程与Reactor模型实践

本专题围绕 C++ 在高性能网络服务开发中的应用展开,深入讲解 Socket 编程、多路复用机制、Reactor 模型设计原理以及线程池协作策略。内容涵盖 epoll 实现机制、内存管理优化、连接管理策略与高并发场景下的性能调优方法。通过构建高并发网络服务器实战案例,帮助开发者掌握 C++ 在底层系统与网络通信领域的核心技术。

31

2026.03.03

Golang 测试体系与代码质量保障:工程级可靠性建设
Golang 测试体系与代码质量保障:工程级可靠性建设

Go语言测试体系与代码质量保障聚焦于构建工程级可靠性系统。本专题深入解析Go的测试工具链(如go test)、单元测试、集成测试及端到端测试实践,结合代码覆盖率分析、静态代码扫描(如go vet)和动态分析工具,建立全链路质量监控机制。通过自动化测试框架、持续集成(CI)流水线配置及代码审查规范,实现测试用例管理、缺陷追踪与质量门禁控制,确保代码健壮性与可维护性,为高可靠性工程系统提供质量保障。

81

2026.02.28

热门下载

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

精品课程

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

共61课时 | 4.3万人学习

Java 教程
Java 教程

共578课时 | 79.9万人学习

oracle知识库
oracle知识库

共0课时 | 0.6万人学习

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

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