【DB筆試面試666】在Oracle中,在高併發、高負載的狀況下,如何給表添加字段並設置DEFAULT值?...

640?wx_fmt=gif

         程序員

題目         部分面試

在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來更新,這樣不至於大批量鎖表,請參考本書中分批更新的部分【】。以下所示:ide

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;
 2
 3BANNER
 4--------------------------------------------------------------------------------
 5Oracle Database 11g Enterprise Edition Release 11.2.0.3.0 - 64bit Production
 6
 7LHR@OCPLHR1> set time on
 816:59:00 LHR@OCPLHR1> set timing on
 916: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;
 2
 3  COUNT(*)
 4----------
 5    200000
 6
 7Elapsed: 00:00:00.02
 817: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);
 2
 3Index created.
 4
 5Elapsed: 00:00:00.71
 617:31:08 LHR@OCPLHR1> update t1 set c_ddl2='8881' where rownum<=1;
 7
 81 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 120
  2LHR@lhr121> select * from v$version where rownum<=1;
  3
  4BANNER                                                                               CON_ID
  5-------------------------------------------------------------------------------- ----------
  6Oracle Database 12c Enterprise Edition Release 12.1.0.2.0 - 64bit Production              0
  7
  8Elapsed: 00:00:00.00
  9LHR@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) cl
 14  6    FROM DUAL
 15  7  CONNECT BY LEVEL <= 100000;
 16
 17Table created.
 18
 19Elapsed: 00:00:09.41
 20LHR@lhr121> SELECT d.bytes FROM user_segments d WHERE d.segment_name='T1';
 21
 22     BYTES
 23----------
 24   4194304
 25
 26Elapsed: 00:00:00.33
 27LHR@lhr121>  ALTER TABLE t1 ADD c_ddl NUMBER DEFAULT 666 ;
 28
 29Table altered.
 30
 31Elapsed: 00:00:00.65
 32LHR@lhr121> SELECT d.bytes FROM user_segments d WHERE d.segment_name='T1';
 33
 34     BYTES
 35----------
 36   4194304
 37
 38Elapsed: 00:00:00.14
 39LHR@lhr121> ALTER TABLE t1 ADD c_ddl2 NUMBER DEFAULT 888 not null;
 40
 41Table altered.
 42
 43Elapsed: 00:00:00.15
 44LHR@lhr121> SELECT d.bytes FROM user_segments d WHERE d.segment_name='T1';
 45
 46     BYTES
 47----------
 48   4194304
 49
 50Elapsed: 00:00:00.09
 51
 52LHR@lhr121> SELECT COUNT(*) FROM t1 WHERE c_ddl2=888;
 53
 54  COUNT(*)
 55----------
 56    100000
 57
 58Elapsed: 00:00:00.02
 59LHR@lhr121>  select  * from table(dbms_xplan.display_cursor);
 60
 61PLAN_TABLE_OUTPUT
 62-----------------------------------------------------------------------------
 63SQL_ID  bq50v8z914juk, child number 1
 64-------------------------------------
 65SELECT COUNT(*) FROM t1 WHERE c_ddl2=888
 66
 67Plan hash value: 3724264953
 68
 69---------------------------------------------------------------------------
 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---------------------------------------------------------------------------
 76
 77Predicate Information (identified by operation id):
 78---------------------------------------------------
 79
 80   2 - filter(NVL("C_DDL2",888)=888)
 81
 82Note
 83-----
 84   - statistics feedback used for this statement
 85
 86
 8723 rows selected.
 88
 89Elapsed: 00:00:00.05
 90
 91LHR@lhr121> SELECT COUNT(*) FROM t1 WHERE c_ddl=666;
 92
 93  COUNT(*)
 94----------
 95    100000
 96
 97Elapsed: 00:00:00.04
 98LHR@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程序員面試筆試寶典》,做者:小麥苗



640?wx_fmt=gif

---------------優質麥課------------

640?wx_fmt=png

詳細內容能夠添加麥老師微信或QQ私聊。


640?wx_fmt=gif


About Me:小麥苗      

● 本文做者:小麥苗,只專一於數據庫的技術,更注重技術的運用

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

● 本系列題目來源於做者的學習筆記,部分整理自網絡,如有侵權或不當之處還請諒解

● 版權全部,歡迎分享本文,轉載請保留出處

● QQ:646634621  QQ羣:618766405

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

● 題目解答如有不當之處,還望各位朋友批評指正,共同進步

640?wx_fmt=gifDBA寶典

長按下圖識別二維碼或微信掃描下圖二維碼來關注小麥苗的微信公衆號:xiaomaimiaolhr,學習最實用的數據庫技術。

640?wx_fmt=gif

640?wx_fmt=gif

640?wx_fmt=png

相關文章
相關標籤/搜索