♣ 程序員
題目 部分面試
在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程序員面試筆試寶典》,做者:小麥苗
詳細內容能夠添加麥老師微信或QQ私聊。
About Me:小麥苗
● 本文做者:小麥苗,只專一於數據庫的技術,更注重技術的運用
● 做者博客地址:http://blog.itpub.net/26736162/abstract/1/
● 本系列題目來源於做者的學習筆記,部分整理自網絡,如有侵權或不當之處還請諒解
● 版權全部,歡迎分享本文,轉載請保留出處
● QQ:646634621 QQ羣:618766405
● 提供OCP、OCM和高可用部分最實用的技能培訓
● 題目解答如有不當之處,還望各位朋友批評指正,共同進步
長按下圖識別二維碼或微信掃描下圖二維碼來關注小麥苗的微信公衆號:xiaomaimiaolhr,學習最實用的數據庫技術。