oracle数据库添加字段不同方式的实验测试

(1)创建测试表

--测试环境下的一张表alter_test,数据量如下

SQL> select count(*) from alter_test;

  COUNT(*)
----------
  37126467

(2)新增字段测试,增加字段不带默认值

给表alter_test新增一个字段:alter table alter_test add  test_column1 char(1);

--执行10046事件
alter session set events '10046 trace name context forever,level 12';

--给表alter_test新增一个字段
alter table alter_test add  test_column1 char(1);

--关闭10046事件
alter session set events '10046 trace name context off';


--获得当前trace文件生成路径
select tracefile from v$process where addr in (select paddr from v$session where sid in (select distinct sid from v$mystat));

--使用oracle server自带的tkprof工具解析trace文件
[oracle@test ~]$ tkprof /U01/app/oracle/diag/rdbms/testdb/testdb/trace/testdb_ora_43138.trc /home/oracle/alter_test.log

TKPROF: Release 11.2.0.3.0 - Development on Sat Apr 25 19:01:05 2020

Copyright (c) 1982, 2011, Oracle and/or its affiliates.  All rights reserved.


[oracle@test ~]$ ll /home/oracle/alter_test.log
-rw-r--r-- 1 oracle oinstall 61531 Apr 25 19:01 /home/oracle/alter_test.log

(3)查看解析的trace文件

从解析后的trace文件可以看到下面的信息:

明此时对表alter_test以NOWAIT方式,加了ROW EXCLUSIVE模式锁。接下来就是执行的新增字段的SQL语句,

直接看关闭10046事件之前的最后一句;

执行的是col$表的更新语句。从表名看,col$是列的数据字典表,使用了绑定变量,那这些值是什么,就成了问题的关键。

使用v$sql_bind_capture可以查看仍在内存中的SQL绑定变量值10046事件前的最后一句SQL使用的绑定变量值如下:

select sql_id,name,datatype_string,last_captured,value_string from v$sql_bind_capture where sql_id='6vqvn8ya0xybh' order by last_captured,position;

从trace文件中可以看到 update col$语句中一共有20个绑定变量,上面SQL显示的绑定变量值同样是20个,说明是对应的。

截图显示的有重复的是因为字段TIESHEN_BIND是测试之前不久新加的字段)。

 update col$的where条件是编号为1和2的绑定变量值,这里显示的是14936和TEST_COLUMN1。再来看看这个14936是什么,从上面SQL的条件obj#=:1,猜测是一个对象,

select object_name,object_id from dba_objects where object_id=14936;

于是,可以猜测,之前已经将TEST_COLUMN1字段加入了相应的数据字典表,最后一句就是更新col$中alter_test表TEST_COLUMN1字段的一些信息。trace文件的最后列出了所有递归调用语句的消耗统计

上面我们了解到了新增字段的SQL语句背后,Oracle大致做了什么操作。接着,我们通过实验来看下不同方式新增字段的效率。

(4)实验一,添加可为空不带默认值的字段

如上刚才的实验我们添加的就是这种形式的字段,时间是比较快速的。

SQL> alter table alter_test add  test_column1 char(1);
 
Table altered
 
Executed in 0.062 seconds

新增一个允许NULL,且无默认值的字段。从10046的trace文件看获得的是一个ROW EXCLUSIVE模式锁

(5)实验二,添加可为空带默认值的字段

alter session set events '10046 trace name context forever,level 12';
alter table alter_test add  test_column2 char(1) default 'N';
alter session set events '10046 trace name context off';

SQL> set timing on
SQL> alter table alter_test add  test_column2 char(1) default 'N';

Table altered.

Elapsed: 00:12:15.37

可以看到时间是12多分钟。分析产生的trace日志如下:

从10046的trace文件看表会首先用EXCLUSIVE模式锁来锁定表。同时,在最后执行了更新字段test_column2为默认值的操作因此不难想像,前台反映的现象就是这个操作处于hang状态,并且影响其他session对该表的操作,为什么耗时这样久,原因就是这个操作需要更新表中所有记录该字段为默认值,另外,还会因为数据量的增加,可能需要更多的UNDO空间,进而可能因为一条新增字段的操作,导致整个库的UNDO表空间不够用,不仅影响对这张表的正常增删改操作(因为获取了最高级别EXCLUSIVE锁),还有可能影响其他业务功能(因为UNDO表空间不够用)。

(6)实验三、添加带默认值且不为空的字段

alter session set events '10046 trace name context forever,level 12';
set timing on
alter table alter_test add  test_column3 char(1) default 'N' not null;
alter session set events '10046 trace name context off';

select tracefile from v$process where addr in (select paddr from v$session where sid in (select sid from v$mystat));

可以看到新增一个包含NOT NULL约束,有默认值的字段用时很短。从10046的trace文件看,会获得一个ROW EXCLUSIVE模式锁来锁定表。

锁的级别比实验要低,而且该默认值是存储于数据字典表中的,并不是保存在原表记录上,即新增一个NOT NULL和默认值的字段,以后每次需要使用该字段时,默认值都是从数据字典中查询到的,这样就减少了新增字段时的DDL语句时间,也减少了存储空间(不用每条需要使用默认值的记录都存储默认值)。

像上面第一次增加列的操作时,会同时更新sys.ecol$和sys.col$数据字典表,若以后再修改这个默认值,则只是会修改sys.col$的值,且以后每次查询也是从sys.col$的default$列获取默认值,我们可以根据sys.ecol$、sys.col$和dba_objects查询相关表和字段信息。

select * from dba_objects where object_name='ALTER_TEST';

SELECT * FROM sys.ecol$;

查看第一次的默认值:

select name,default$ from sys.col$ where obj#=14936 and col#=60;

尝试修改默认值,从N变为Y;

alter table alter_test modify test_column3 default 'Y';

查看修改后sys.col$记录的默认值:

select name,default$ from sys.col$ where obj#=14936 and col#=61;

11g的官方文档也介绍了,Oracle增加了这种新特性,对新增字段操作做了上面这些优化,如果新增一个含有默认值的字段,那么会立即更新每一行,在更新过程中,会有一个EXCLUSIVE级别的锁在该表上。如果指定NOT NULL和默认值,则会进行优化,降低阻止DML操作的时间。

(7)实验四、新增一个字段为not null

alter table alter_test add  test_column4 char(1) not null; 

新增一个仅有NOT NULL约束,没有默认值的字段,则需要表为空。如果增加一个仅有NOT NULL的约束字段,那么需要表不能包含任何记录,否则就需要必须指定一个默认值,这也好理解,如果执行之前有记录,又要求NOT NULL,那么之前的记录字段默认值是什么就需要指定才行。

(8)实验五、删除表字段

alter session set events '10046 trace name context forever,level 12';
set timing on
alter table alter_test drop column test_column3;
alter session set events '10046 trace name context off';
select tracefile from v$process where addr in (select paddr from v$session where sid in (select sid from v$mystat));

从10046的trace文件看,也是获得了一个EXCLUSIVE锁,进而在删除字段的过程中是对整张表的DML操作有影响的。

小结:

1. 11g以上的版本,如果使用NOT NULL和默认值的方式新增字段,那么执行时间会大大降低。且只会有一个ROW EXCLUSIVE级别锁。

2. 11g以上的版本,如果使用默认值,没有NOT NULL约束的方式新增字段,那么执行时间会很久,取决于表中数据量的大小,获得的是EXCLUSIVE级别锁,期间会影响所有记录的DML操作,可能会因UNDO不足对其他操作有影响。

3. 11g以上的版本,如果新增字段没有默认值,也没有NOT NULL约束,则还是会使用ROW EXCLUSIVE模式锁,但由于不需要更新字段值,执行时间也是比较短

补充:今天在操作一张数据量7亿左右的表增加带默认值且不为空,耗费时间为0.016秒。并测试了修改数据类型所耗费的时间,记录如下:

表结构如下:
create table USER_TEST
(
  UIN           NUMBER(16) not null,
  IP_ADDR       VARCHAR2(16),
  USER_TIME     DATE,
)


SQL> alter table user_test modify (IP_ADDR varchar2(20));
 
Table altered
 
Executed in 0.031 seconds
 
SQL> alter table user_test modify (UIN NUMBER(20));
 
Table altered
 
Executed in 0.015 seconds


现在表user_test中字段USER_TIME的值有为null的,执行如下语句会报错:

SQL> alter table user_test modify USER_TIME default sysdate not null;
 
alter table user_test modify USER_TIME default sysdate not null
 
ORA-02296: 无法启用 (.) - 找到空值

因此:对于表字段的数据已经存在空值,就不能修改为not null约束了,会报错。

如果直接修改update_time字段有默认值,则只对新插入的数据有效。

alter table user_test modify USER_TIME default sysdate;

修改字段的默认值,只对新插入的数据有效。

 

 

 

 

 

 

 

 

 

 

 

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

#慧#

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值