Oracle之大表DDL

[TOC]

Oracle之大表DDL操作那些事

场景:万级以上数据量在给表增加字段的时候,可以随意执行Alter吗?

下面我们来看这条Alter语句:

  1. 给表增加字段并设置默认值

alter table table_name add (text_type NUMBER(1) default 0 NOT NULL);

DDL原理:这条DDL执行,其实是先给表增加一列(不设置默认值及NOT NULL),然后给执行全表的update,更新这个字段为默认值,最终在设置这个字段的默认值及非空。

如果各位有大数据量的表,可以使用表备份语句建立备份表,并进行上方这条DDL测试,Alter期间可以通过锁相关查询语句,看看产生了几级的锁。

-- 建表结构+复制数据
create table newtable as select * from oldtable;
--查出oracle当前的被锁对象
SELECT l.session_id sid,  
    l.locked_mode 锁模式,  
    l.oracle_username 登录用户,  
    l.os_user_name 登录机器用户名,  
    s.machine 机器名,  
    s.terminal 终端用户名,  
    o.object_name 被锁对象名,  
    s.logon_time 登录数据库时间  
FROM v$locked_object l, all_objects o, v$session s  
WHERE l.object_id = o.object_id  
AND l.session_id = s.sid;

很明显,全表的Update肯定会产生行锁也就是3级RX锁,这样数据量越大,处理时间变长,事务也越大。业务在处理DML语句时,会因目标行被这里的操作锁住而产生阻塞等待,那么应用程序也就阻塞了,相当于人工手动停机,这样的事肯定没有一个人愿意看见,那么怎么处理合适呢?

方案一

首先,分析原因,造成锁表的原因是全表更新,产生的大事务导致,那么我们如果把事务拆解下来,分割一个个的小事务,这样就可以有效的规避全表的行锁了,业务应用层也不会产生阻塞。

既然有了思路,就搞起来。

整体的思路是这样的:

对应Sql,其中err_log用来记录错误信息,以供后续分析,如果执行没问题,记得删除此表。

其中上述游标语句也可以写出如下:

好,到此差不多已经讲完了这个问题,但是平常工作中,我们在没有必要给这个字段加上非空要求时,就可以采用以下的sql,来增加新列,也就不会造成上述说的问题。是因为修改列增加默认值,只会对新插入的数据生效,而历史的数据,是不会处理的。

方案二

oracle10G及以上可以使用在线重定义的方式进行。

官方文档:点击进入

Oracle提供了常量供大家使用:

权限要求

在线重定义的思路

原理:通过对中间表(即想要修改原表的最终结构)执行大量DML复制原表的数据,实现原表的零锁表,以达到不影响用户的操作。最终在结束重定义前,对中间表按照原表建立一套索引等(刚开始建立中间表时,不建议建立索引,会增加重定义的时间,每次DML都需要维护索引),以上都结束后,执行结束重定义,这一步的内部过程会再次同步下数据(即执行锁定同步数据,会锁表,由于数据较少,锁定时间不会太长),数据同步结束后,将表名互换。

实现步骤

原表DDL:

需要给原表增加 flag 字段

  1. 决定重定义模式

    默认是主键模式重定义,如果未报错,则可以使用主键模式,否则使用ROWID模式重定义(取决于原表是否有主键)

  2. 建立中间表

  3. 开始重定义

  4. 给中间表建立索引等约束

  5. 锁定同步数据(可选)

  6. 结束重定义

  7. 删除中间表

  8. (可选)重命名索引、触发器和约束 对于采用了ROWID方式重定义的表,包括了一个隐含列M_ROW$$。推荐使用下列语句经隐含列置为UNUSED状态或删除。

注意:如果执行过程中出现错误或者人为选择退出的话,可以执行DBMS_REDEFINITION.ABORT_REDEF_TABLE() 过程。

Last updated