题目部分

在Oracle中,在高并发、高负载的情况下,如何给表添加字段并设置DEFAULT值?

答案部分

在Oracle 12c之前,当Oracle表数据量上亿时,对表执行“ALTER TABLE XXX ADD COLUMN_XX VARCHAR2(2) DEFAULT 'XXX';”操作时,效率及安全性是必须要考虑的因素。若直接执行,则会在该过程中给表加上6级表锁,也就是连查询都需要等待,这在生产库上是相当危险的操作。因为Oracle在执行上述操作过程中,不仅要更新数据字典,还会刷新全部的记录,并且会使得Undo表空间暴涨,所以,正确的做法是将更新数据字典和更新字段值分开。

例如,表LKILL.T_KILL约有4500W的数据,直接添加一个字段C_LHR需要花费21分钟,如下所示:

112:20:17 SYS@RACLHR2> ALTER TABLE LKILL.T_KILL ADD C_LHR VARCHAR2(100) DEFAULT 'LHR';
2Table altered.
3Elapsed: 00:21:58.53

若修改为如下的方式,则可以显著提高这个操作的性能,但表中原有的记录对于新添加的列为空,新增记录默认值会设置为LHR,那么原有记录的默认值就需要在系统空闲的时候进行批量更新、批量提交或采用系统包DBMS_PARALLEL_EXECUTE来更新,这样不至于大批量锁表,请参考本书中分批更新的部分【】。如下所示:

112:42:17 SYS@RACLHR2> ALTER TABLE LKILL.T_KILL ADD A_LHR VARCHAR2(100);
2Table altered.
3Elapsed: 00:00:00.35
413:53:54 SYS@RACLHR2> ALTER TABLE LKILL.T_KILL MODIFY A_LHR VARCHAR2(100) DEFAULT 'LHR';
5Table altered.
6Elapsed: 00:00:00.06

需要注意的是,从Oracle 11g开始,当添加一个带有默认值的非空列时(注意2个条件,NOT NULL和默认值),Oracle不会使用这个默认值来物理更新现有存在的行,Oracle只会存储这个新列元数据(NOT NULL约束和DEFAULT默认值),从而使得对该表的添加带有默认值的非空列操作可以在瞬间完成。当然,从表中检索该列时,会有部分的NVL函数代价。具体的细微差别可以通过10046事件来分析,这里不再详细解析。

从Oracle 12c开始,支持具有默认值的空列的添加列的DDL语句优化,即如下2条SQL语句的效率是一样的,也不存在锁表的现象了:

1ALTER TABLE LKILL.T_KILL ADD A_LHR VARCHAR2(100);
2ALTER TABLE LKILL.T_KILL ADD A_LHR VARCHAR2(100) NOT NULL;

示例如下所示:

 1LHR@OCPLHR1> select * from v$version where rownum<=1;23BANNER4--------------------------------------------------------------------------------5Oracle Database 11g Enterprise Edition Release 11.2.0.3.0 - 64bit Production67LHR@OCPLHR1> set time on816:59:00 LHR@OCPLHR1> set timing on916:59:08 LHR@OCPLHR1> CREATE TABLE t1 AS
1016:59:21   2  SELECT ROWNUM N1,
1116:59:21   3         TRUNC((ROWNUM - 1) / 3) N2,
1216:59:21   4         TRUNC(DBMS_RANDOM.VALUE(ROWNUM, ROWNUM * 10)) N3,
1316:59:21   5         DBMS_RANDOM.STRING('U', 10) cl
1416:59:21   6    FROM DUAL
1516:59:21   7  CONNECT BY LEVEL <= 200000;
16
17Table created.
18
19Elapsed: 00:00:05.72
20
2116:59:45 LHR@OCPLHR1> SELECT d.bytes FROM user_segments d WHERE d.segment_name='T1';
22
23     BYTES
24----------
25   7340032
26
27Elapsed: 00:00:00.09
2817:01:00 LHR@OCPLHR1> ALTER TABLE t1 ADD c_ddl NUMBER DEFAULT 666 ;
29
30Table altered.
31
32Elapsed: 00:00:25.29
3317:02:07 LHR@OCPLHR1> SELECT d.bytes FROM user_segments d WHERE d.segment_name='T1';
34
35     BYTES
36----------
37   8388608
38
39Elapsed: 00:00:00.01
4017:02:13 LHR@OCPLHR1> ALTER TABLE t1 ADD c_ddl2 NUMBER DEFAULT 888 not null;
41
42Table altered.
43
44Elapsed: 00:00:00.08
4517:02:37 LHR@OCPLHR1> SELECT d.bytes FROM user_segments d WHERE d.segment_name='T1';
46
47     BYTES
48----------
49   8388608
50
51Elapsed: 00:00:00.01

可以看出,在Oracle 11g中,加了NOT NULL约束的SQL语句,可以在瞬间完成添加列的操作,而只设置了默认值的SQL语句使用了25秒的时间。另外,加了NOT NULL约束的SQL语句执行完毕后,表的大小没有变化,这也说明了Oracle并没有做物理更新。

下面查看其执行计划,注意在这里不要使用“SET AUTOT ON”的方式,否则不能看到其真实的执行计划:

 117:05:30 LHR@OCPLHR1> SELECT COUNT(*) FROM t1 WHERE c_ddl2=888;23  COUNT(*)4----------5    20000067Elapsed: 00:00:00.02817:05:39 LHR@OCPLHR1> select  * from table(dbms_xplan.display_cursor);9
10PLAN_TABLE_OUTPUT
11-------------------------------------------------------------
12SQL_ID  bq50v8z914juk, child number 0
13-------------------------------------
14SELECT COUNT(*) FROM t1 WHERE c_ddl2=888
15
16Plan hash value: 3724264953
17
18---------------------------------------------------------------------------
19| Id  | Operation          | Name | Rows  | Bytes | Cost (%CPU)| Time     |
20---------------------------------------------------------------------------
21|   0 | SELECT STATEMENT   |      |       |       |   282 (100)|          |
22|   1 |  SORT AGGREGATE    |      |     1 |    13 |            |          |
23|*  2 |   TABLE ACCESS FULL| T1   |   199K|  2530K|   282   (2)| 00:00:04 |
24---------------------------------------------------------------------------
25
26Predicate Information (identified by operation id):
27---------------------------------------------------
28
29   2 - filter(NVL("C_DDL2",888)=888)
30
31Note
32-----
33   - dynamic sampling used for this statement (level=2)
34
35
3623 rows selected.
37
3817:08:55 LHR@OCPLHR1> SELECT * FROM t1 WHERE rownum<=1;
39
40        N1         N2         N3 CL              C_DDL     C_DDL2
41---------- ---------- ---------- ---------- ---------- ----------
42         1          0          8 XYGGZXRRYR        666        888

可以看到,在谓词部分出现了NVL函数。所以,Oracle认为C_DDL2列是空列。

下面测试是否可以使用索引:

 117:29:24 LHR@OCPLHR1> CREATE INDEX idx_c_ddl2 ON t1(c_ddl2);23Index created.45Elapsed: 00:00:00.71617:31:08 LHR@OCPLHR1> update t1 set c_ddl2='8881' where rownum<=1;781 row updated.9
10Elapsed: 00:00:00.05
1117:31:13 LHR@OCPLHR1> commit;
12
13Commit complete.
14
15Elapsed: 00:00:00.00
1617:31:16 LHR@OCPLHR1> SELECT * FROM t1 WHERE c_ddl2=8881;
17
18        N1         N2         N3 CL              C_DDL     C_DDL2
19---------- ---------- ---------- ---------- ---------- ----------
20         1          0          8 XYGGZXRRYR        666       8881
21
22Elapsed: 00:00:00.01
2317:31:24 LHR@OCPLHR1> select  * from table(dbms_xplan.display_cursor);
24
25PLAN_TABLE_OUTPUT
26-------------------------------------------------------------------------------
27SQL_ID  0sm5s7zkvycrq, child number 0
28-------------------------------------
29SELECT * FROM t1 WHERE c_ddl2=8881
30
31Plan hash value: 1464185165
32
33------------------------------------------------------------------------------------------
34| Id  | Operation                   | Name       | Rows  | Bytes | Cost (%CPU)| Time     |
35------------------------------------------------------------------------------------------
36|   0 | SELECT STATEMENT            |            |       |       |     2 (100)|          |
37|   1 |  TABLE ACCESS BY INDEX ROWID| T1         |     1 |    34 |     2   (0)| 00:00:01 |
38|*  2 |   INDEX RANGE SCAN          | IDX_C_DDL2 |     1 |       |     1   (0)| 00:00:01 |
39------------------------------------------------------------------------------------------
40
41Predicate Information (identified by operation id):
42---------------------------------------------------
43
44   2 - access("C_DDL2"=8881)
45
46
4719 rows selected.
48
49Elapsed: 00:00:00.11

令人惊喜的是,使用了索引。

下面看看在Oracle 12c中的执行情况:

  1LHR@lhr121> set line 1202LHR@lhr121> select * from v$version where rownum<=1;34BANNER                                                                               CON_ID5-------------------------------------------------------------------------------- ----------6Oracle Database 12c Enterprise Edition Release 12.1.0.2.0 - 64bit Production              078Elapsed: 00:00:00.009LHR@lhr121> CREATE TABLE t1 AS 10  2  SELECT ROWNUM N1,11  3         TRUNC((ROWNUM - 1) / 3) N2,12  4         TRUNC(DBMS_RANDOM.VALUE(ROWNUM, ROWNUM * 10)) N3,13       DBMS_RANDOM.STRING('U', 10) cl14  6    FROM DUAL15  7  CONNECT BY LEVEL <= 100000;1617Table created.1819Elapsed: 00:00:09.4120LHR@lhr121> SELECT d.bytes FROM user_segments d WHERE d.segment_name='T1';2122     BYTES23----------24   41943042526Elapsed: 00:00:00.3327LHR@lhr121>  ALTER TABLE t1 ADD c_ddl NUMBER DEFAULT 666 ;2829Table altered.3031Elapsed: 00:00:00.6532LHR@lhr121> SELECT d.bytes FROM user_segments d WHERE d.segment_name='T1';3334     BYTES35----------36   41943043738Elapsed: 00:00:00.1439LHR@lhr121> ALTER TABLE t1 ADD c_ddl2 NUMBER DEFAULT 888 not null;4041Table altered.4243Elapsed: 00:00:00.1544LHR@lhr121> SELECT d.bytes FROM user_segments d WHERE d.segment_name='T1';4546     BYTES47----------48   41943044950Elapsed: 00:00:00.095152LHR@lhr121> SELECT COUNT(*) FROM t1 WHERE c_ddl2=888;5354  COUNT(*)55----------56    1000005758Elapsed: 00:00:00.0259LHR@lhr121>  select  * from table(dbms_xplan.display_cursor);6061PLAN_TABLE_OUTPUT62-----------------------------------------------------------------------------63SQL_ID  bq50v8z914juk, child number 164-------------------------------------65SELECT COUNT(*) FROM t1 WHERE c_ddl2=8886667Plan hash value: 37242649536869---------------------------------------------------------------------------70| Id  | Operation          | Name | Rows  | Bytes | Cost (%CPU)| Time     |71---------------------------------------------------------------------------72|   0 | SELECT STATEMENT   |      |       |       |   122 (100)|          |73|   1 |  SORT AGGREGATE    |      |     1 |    13 |            |          |74|*  2 |   TABLE ACCESS FULL| T1   |   100K|  1269K|   122   (1)| 00:00:01 |75---------------------------------------------------------------------------7677Predicate Information (identified by operation id):78---------------------------------------------------7980   2 - filter(NVL("C_DDL2",888)=888)8182Note83-----84   - statistics feedback used for this statement85868723 rows selected.8889Elapsed: 00:00:00.059091LHR@lhr121> SELECT COUNT(*) FROM t1 WHERE c_ddl=666;9293  COUNT(*)94----------95    1000009697Elapsed: 00:00:00.0498LHR@lhr121>  select  * from table(dbms_xplan.display_cursor);99
100PLAN_TABLE_OUTPUT
101------------------------------------------------------------------------------------------------------------------------
102SQL_ID  dph2gfp6f0jja, child number 1
103-------------------------------------
104SELECT COUNT(*) FROM t1 WHERE c_ddl=666
105
106Plan hash value: 3724264953
107
108---------------------------------------------------------------------------
109| Id  | Operation          | Name | Rows  | Bytes | Cost (%CPU)| Time     |
110---------------------------------------------------------------------------
111|   0 | SELECT STATEMENT   |      |       |       |   122 (100)|          |
112|   1 |  SORT AGGREGATE    |      |     1 |    13 |            |          |
113|*  2 |   TABLE ACCESS FULL| T1   |  1000 | 13000 |   122   (1)| 00:00:01 |
114---------------------------------------------------------------------------
115
116Predicate Information (identified by operation id):
117---------------------------------------------------
118
119   2 - filter(DECODE(TO_CHAR(SYS_OP_VECBIT("SYS_NC00005$",0)),NULL,NVL("
120              C_DDL",666),'0',NVL("C_DDL",666),'1',"C_DDL")=666)
121
122
12320 rows selected.
124
125Elapsed: 00:00:00.12
126LHR@lhr121> SELECT d.column_name, d.column_id,d.hidden_column,d.virtual_column FROM Dba_Tab_Cols d  WHERE d.table_name='T1' order by column_id;
127
128COLUMN_NAME      COLUMN_ID HID VIR
129--------------- ---------- --- ---
130N1                       1 NO  NO
131N2                       2 NO  NO
132N3                       3 NO  NO
133CL                       4 NO  NO
134C_DDL                    5 NO  NO
135C_DDL2                   6 NO  NO
136SYS_NC00005$               YES NO
137
1387 rows selected.
139
140Elapsed: 00:00:00.32
141LHR@lhr121>

从示例可以清楚地看到,在Oracle 12c中,添加具有默认值的DDL优化已扩展到包括默认值的空列。Oracle使用了一个未公开的函数SYS_OP_VECBIT和新的隐藏列SYS_NC00005$,因为该列没有被物理更新。

& 说明:

有关给表添加列的更多内容可以参考我的博客:http://blog.itpub.net/26736162/viewspace-2151015/

有关批量更新和DBMS_PARALLEL_EXECUTE的使用更详细的内容可以参考我的BLOG:① http://blog.itpub.net/26736162/viewspace-2140626/ ②http://blog.itpub.net/26736162/viewspace-1684396

本文选自《Oracle程序员面试笔试宝典》,作者:小麦苗

---------------优质麦课------------

详细内容可以添加麦老师微信或QQ私聊。

About Me:小麦苗

● 本文作者:小麦苗,只专注于数据库的技术,更注重技术的运用

● 作者博客地址:http://blog.itpub.net/26736162/abstract/1/

● 本系列题目来源于作者的学习笔记,部分整理自网络,若有侵权或不当之处还请谅解

● 版权所有,欢迎分享本文,转载请保留出处

● QQ:646634621  QQ群:618766405

● 提供OCP、OCM和高可用部分最实用的技能培训

● 题目解答若有不当之处,还望各位朋友批评指正,共同进步

DBA宝典

长按下图识别二维码或微信扫描下图二维码来关注小麦苗的微信公众号:xiaomaimiaolhr,学习最实用的数据库技术。

【DB笔试面试666】在Oracle中,在高并发、高负载的情况下,如何给表添加字段并设置DEFAULT值?...相关推荐

  1. oracle创建表同时给值,在高并发、高负载的情况下,如何给表添加字段并设置DEFAULT值?...

    在Oracle 12c之前,当Oracle表数据量上亿时,对表执行"ALTER TABLE XXX ADD COLUMN_XX VARCHAR2(2) DEFAULT 'XXX';" ...

  2. 【DB笔试面试594】在Oracle中,关键字NOLOGGING、APPEND和PARALLEL提高DML性能方面有什么差别?...

    ♣题目部分 在Oracle中,关键字NOLOGGING.APPEND和PARALLEL提高DML性能方面有什么差别? ♣答案部分 众所周知,表模式(LOGGING/NOLOGGING).插入模式(AP ...

  3. 【DB笔试面试618】在Oracle中,“OR扩展”可以有查询转换吗?

    ♣题目 部分 在Oracle中,"OR扩展"可以有查询转换吗? ♣答案部分 同一字段: 1LHR@orclasm > SELECT * FROM SCOTT.EMP A WH ...

  4. 【DB笔试面试593】在Oracle中,表的访问方式有哪几种?

    ♣题目 部分 在Oracle中,表的访问方式有哪几种? ♣答案部分 访问表的方式也叫优化器访问路径,主要有3种访问路径:全表扫描(FULL TABLE SCAN,FTS).索引扫描(INDEX SCA ...

  5. 【DB笔试面试164】在Oracle中,如何彻底停止expdp数据泵进程?

    [DB笔试面试164]在Oracle中,如何彻底停止expdp数据泵进程? 真题1. 如何彻底停止 expdp 进程? 答案:许多同事在使用expdp命令时,不小心按了CTRL+C组合键,然后又输入e ...

  6. 【知识点整理】Oracle中NOLOGGING、APPEND、ARCHIVE和PARALLEL下,REDO、UNDO和执行速度的比较...

    [知识点整理]Oracle中NOLOGGING.APPEND.ARCHIVE和PARALLEL下,REDO.UNDO和执行速度的比较 [知识点整理]Oracle中NOLOGGING.APPEND.AR ...

  7. net 模式中虚拟机连不上本机oracle_高并发与负载均衡(三种负载模式)

    随着互联网的飞速发展,传统的昂贵的大容量高性能服务器(F5 BIG-IP.Citrix NetScaler.A10)已经越来越应付不了日益增长的业务需求了,而高并发和负载均衡所带来的高可靠/高可用/低 ...

  8. 在Python中给定包含该项目的列表的情况下查找项目的索引

    对于列表["foo", "bar", "baz"]和列表"bar" ,如何在Python中获取其索引(1)? #1楼 a ...

  9. 给oracle数据库现有表添加字段

    给表添加字段: 语法:alter table tablename add (column1 datatype [default value][null/not null],column2 dataty ...

  10. 按如下函数原型编程从键盘输入一个m行n列的二维数组,然后计算数组中元素的最大值及其所在的行列下标值。其中,m和n的值由用户键盘输入。已知m和n的值都不超过10。

    题目:按如下函数原型编程从键盘输入一个m行n列的二维数组,然后计算数组中元素的最大值及其所在的行列下标值.其中,m和n的值由用户键盘输入.已知m和n的值都不超过10. void InputArray( ...

最新文章

  1. python3+selenium入门08-鼠标事件
  2. linux系统CPU,内存,磁盘,网络流量监控脚本
  3. leetcode-cli 用命令行开心的刷算法
  4. C++设计模式实现--策略(Strategy)模式
  5. JSON.stringify 语法实例讲解
  6. python的高阶函数
  7. SpringCloud04 服务配置中心、消息总线、远程配置动态刷新
  8. 2011版计算机网络原理,2011年7月自考04741计算机网络原理真题及答案
  9. centos 7 单独安装mysql和mysqli和pdo_mysql扩展
  10. 每日算法系列【LeetCode 523】连续的子数组和
  11. 福利 | 简历模板大放送
  12. 鸢尾花(Iris)数据集
  13. 「 程序员的风险控制」意外险:花几十块就能让你不用担心明天和意外哪个先来
  14. PHP:pack、unpack用法大全
  15. 使用npm安装下载jQuery
  16. 分享四个一键生成神器:Logo、App、小程序、H5等五分钟快速搞定
  17. 人像分割之ExtremeC3Net
  18. ios10之后的一些新特性
  19. 充电宝GB4943检测标准与项目介绍
  20. mySQL 事物提交成功不等于数据保存成功

热门文章

  1. c#计算圆或者圆柱面积体积代码
  2. linux桌面不能打开终端,Ubuntu无法用快捷键或图标打开终端
  3. 大数据下无隐私APP为何要用户摄像头麦克风通讯录等全权限才服务(公号回复“无隐私APP”下载PDF彩标典藏版资料,欢迎转发赞赏)
  4. 字符串--------KMP算法(studying)
  5. 手机端用云服务器文件在哪里设置,如何使用FolderSync在安卓手机上同步文件夹到坚果云? | 坚果云帮助中心...
  6. 开机自动启动程序的操作(就是这么简单)
  7. Android学习笔记3-开发过程中一些常用操作
  8. android自动循环播放视频,Android编程实现VideoView循环播放功能的方法
  9. [EdgeAI] NXP eIQ 机器学习Toolkit (二):模型篇
  10. 标题:《东邪西毒》台词全本(国语版) (转)