Oracle兩個使用細節知識點
大量的Oracle知識點都是需要在實際中不斷發現實驗獲得的。本篇中介紹兩個實際中發現的問題,權當記錄。
1、慎用for update大規模資料
Select for update是我們有時候會使用的一種語句型別。它的特點優勢是能夠將篩選出的資料進行獨佔鎖定。通常情況下的Oracle select操作,是不會對資料表進行鎖定,同時也不會被其他操作所阻塞。
在實際應用中,我們的確是經常使用for update進行資料鎖定。常用的場景是,確定符合條件的資料是否存在,如果存在就立即鎖定該條記錄獨佔。之後對獨佔的資料進行修改。在存在多會話併發訪問的時候,for update可以起到資源/訊號量搶奪的作用。
但是,for update的使用是有相應的代價的。在之前的系列文章中,我們探討過for update要消耗更多的時間,主要在於需要在每個資料行進行加鎖操作。在Oracle11g中,CBO的Query Optimizer採用了一些最佳化技術進行效能提升。但是,總的來說,for update是要消耗更多的資源的。
除了上面提到的方面,在redo size上for update也是有獨特之處。通常我們繼續DDL和更改型別操作的時候,才會生成redo log片段。普通的select是不會生成redo log記錄的。如果使用for update,我們是可以發現select會生成redo size。
SQL> select * from v$version;
BANNER
--------------------------------------------------------------------------------
Oracle Database 11g Enterprise Edition Release 11.2.0.1.0 - Production
PL/SQL Release 11.2.0.1.0 - Production
CORE 11.2.0.1.0 Production
TNS for Linux: Version 11.2.0.1.0 - Production
NLSRTL Version 11.2.0.1.0 – Production
使用v$mystat可以看到當前會話累計的各種指標統計量。
SQL> select a.statistic#, name, value from v$mystat a left join v$statname b on a.STATISTIC#=b.STATISTIC# where b.NAME = 'redo size';
STATISTIC# NAME VALUE
---------- -------------------- ----------
169 redo size 0
當前,redo size生成的消耗量是0。
SQL> select bytes, blocks from dba_segments where segment_name='T' and wner='SYS';
BYTES BLOCKS
---------- ----------
9437184 1152
SQL> select count(*) from t;
COUNT(*)
----------
72349
此時,我們使用一個for update語句。
SQL> select * from t where wner='SCOTT' for update;
(結果集合省略……)
此時,redo size的消耗可以查詢到。
SQL> select a.statistic#, name, value from v$mystat a left join v$statname b on a.STATISTIC#=b.STATISTIC# where b.NAME = 'redo size';
STATISTIC# NAME VALUE
---------- -------------------- ----------
169 redo size 0
SQL> commit;
SQL> select a.statistic#, name, value from v$mystat a left join v$statname b on a.STATISTIC#=b.STATISTIC# where b.NAME = 'redo size';
STATISTIC# NAME VALUE
---------- -------------------- ----------
169 redo size 2628
一個一般的for update之後,進行commit,沒有發生本質上的資料變化。但是之後,我們發現了redo size指標增加到了2628。一般的select操作,就不會產生redo size消耗。
SQL> select * from t where wner='SCOTT';
(結果集合省略……
SQL> select a.statistic#, name, value from v$mystat a left join v$statname b on a.STATISTIC#=b.STATISTIC# where b.NAME = 'redo size';
STATISTIC# NAME VALUE
---------- -------------------- ----------
169 redo size 2628
SQL> commit;
SQL> select a.statistic#, name, value from v$mystat a left join v$statname b on a.STATISTIC#=b.STATISTIC# where b.NAME = 'redo size';
STATISTIC# NAME VALUE
---------- -------------------- ----------
169 redo size 2628
可見,一般的select的操作是不會產生redo記錄的。這個問題我們也可以使用autotrace工具來檢視到。
SQL> set autotrace traceonly;
SQL> select * from t for update;
已選擇72349行。
執行計劃
----------------------------------------------------------
Plan hash value: 250223868
----------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
----------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 72349 | 6853K| 282 (1)| 00:00:04 |
| 1 | FOR UPDATE | | | | | |
| 2 | BUFFER SORT | | | | | |
| 3 | TABLE ACCESS FULL| T | 72349 | 6853K| 282 (1)| 00:00:04 |
----------------------------------------------------------------------------
統計資訊
----------------------------------------------------------
46 recursive calls
73649 db block gets
1066 consistent gets
1026 physical reads
15086508 redo size
5649675 bytes sent via SQL*Net to client
53429 bytes received via SQL*Net from client
4825 SQL*Net roundtrips to/from client
1 sorts (memory)
0 sorts (disk)
72349 rows processed
SQL> select * from t;
已選擇72349行。
執行計劃
----------------------------------------------------------
Plan hash value: 1601196873
--------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
--------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 72349 | 6853K| 282 (1)| 00:00:04 |
| 1 | TABLE ACCESS FULL| T | 72349 | 6853K| 282 (1)| 00:00:04 |
--------------------------------------------------------------------------
統計資訊
----------------------------------------------------------
1 recursive calls
0 db block gets
5788 consistent gets
0 physical reads
0 redo size
3286309 bytes sent via SQL*Net to client
53429 bytes received via SQL*Net from client
4825 SQL*Net roundtrips to/from client
0 sorts (memory)
0 sorts (disk)
72349 rows processed
從統計量資訊看,使用for update在處理相同資料行資料的時候,要產生相當資料量的redo size和巢狀SQL。
分析原因,從Oracle角度看,實現資料行的鎖定依靠Select語句是不能做到的。所以,在for update操作的時候,Oracle會啟動一個事務過程,將篩選出的資料行作出修改動作。
在這個過程中,Oracle會在條件記錄對應的資料塊上的事務槽作出標記,表示對應的資料行已經進行修改。這樣才可能以獨佔的方式獲取資料行。在Oracle中,只要對資料塊進行了修改,無論這個修改是否最終被commit/rollback,都會生成出redo log資訊,寫入到online redo log中。
資料表越大,涉及的資料越多,for update的損耗和生成redo就越大。所以,如果資料表很大,同時select出的資料集合也很大,頻繁的select for update就引起Oracle產生出大量的redo blocks。在歸檔模式下,過於頻繁的對大資料進行for update操作,會帶來額外的歸檔日誌儲存要求。如果沒有預先的規劃,容易引起資料庫hange住。
2、資料列預設值:先加後加,大不相同
資料列預設值,也是我們經常使用的一種資料庫物件技術。透過資料列預設值的設定,當插入資料的時候,如果不對資料列進行指定,那麼預設值就會加入其中。
如果對一個已經存在資料的資料表加列,同時確定預設值和之後設定預設值有什麼差別呢?
SQL> create table t (id number, name varchar2(10));
Table created
SQL> select * from t;
ID NAME
---------- ----------
SQL> insert into t select object_id, null from dba_objects where rownum<10;
9 rows inserted
SQL> commit;
Commit complete
SQL> select * from t;
ID NAME
---------- ----------
20
46
28
15
9 rows selected
如果我們首先建立資料列和定義預設值在一起,會如何呢?
SQL> set timing on;
SQL> alter table t add vname varchar2(10) default 'X';
Table altered
Executed in 0.39 seconds
SQL> select * from t;
ID NAME VNAME
---------- ---------- ----------
20 X
46 X
28 X
15 X
29 X
3 X
25 X
41 X
54 X
9 rows selected
Executed in 0.093 seconds
如果我們在新新增列的時候,指定default value。效果就是將資料列加入其中,並且使用預設值填滿已經為null的記錄專案。Default value的含義在於補全,將不完全的資料加以不全。
不過,也要注意這個操作的時候消耗為0.39s,時間消耗略大。下面,如果我們只是指定資料列,不同時進行預設值指定的情況呢?
--新新增列
SQL> alter table t add nname varchar2(10);
Table altered
Executed in 0.062 seconds
--後設定預設值;
SQL> alter table t modify nname default 'M';
Table altered
Executed in 0.062 seconds
SQL> select * from t;
ID NAME VNAME NNAME
---------- ---------- ---------- ----------
20 X
46 X
28 X
15 X
29 X
3 X
25 X
41 X
54 X
9 rows selected
Executed in 0.094 seconds
第一個區別,如果後設定預設值,資料列中原來為空的取值,是不會如同時設定一樣補齊資料的。
SQL> insert into t (id) values (100);
1 row inserted
Executed in 0 seconds
SQL> commit;
Commit complete
Executed in 0.015 seconds
SQL> select * from t;
ID NAME VNAME NNAME
---------- ---------- ---------- ----------
41 X
54 X
100 X M
10 rows selected
Executed in 0.094 seconds
從實驗結果上看,對新插入的資料,後設定default value是其效果的。同時,從時間上看,一次性的設定新列和預設值消耗的時間巨大。這對於生產環境是一個潛在的風險。
我們對高消耗的操作通常的處理是均衡化負載,儘可能將一個操作劃分為若干個可干預、可控制的操作。所以,對一個大表新增有預設值資料列,可以先加一個無預設值資料列,之後修改預設值,最後使用dml進行資料更新。
3、結論
本篇闡述介紹的兩個知識點,都是一些非常零碎的經驗積累。只有將這些知識透過實驗進行驗證,歸納為自己的所得,不斷的總結提高,才能達到我們質變的目的。
來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/17203031/viewspace-707979/,如需轉載,請註明出處,否則將追究法律責任。
相關文章
- css細節知識點CSS
- TCP 中的兩個細節點TCP
- JS基礎細節知識JS
- Python——基礎知識細節Python
- Oracle細節及難點總結Oracle
- Android的細節知識彙總系列(一)Android
- Oracle 選擇題知識點整理Oracle
- Tungsten Fabric知識庫丨測試2000個vRouter節點部署VR
- 例項總結Oracle知識點大全Oracle
- 初識python必知的6個知識點Python
- 關於Java兩點需要更新的知識Java
- Git的11個知識點Git
- 基於DDD知識揭示Go中結構指標兩個優點Go指標
- Docker 19.03.13的四個使用細節Docker
- Android Service和IntentService知識點詳細總結AndroidIntent
- 兩個連結串列的第一個公共節點
- Oracle RAC修改網路配置知識點彙總Oracle
- Bootstrap 個人學習知識點boot
- jQuery 個人學習知識點jQuery
- Flutter個人小知識點記錄Flutter
- 物化檢視幾個知識點
- 五個UICollectionView常用的知識點UIView
- oracle兩節點RAC,由於gipc導致某節點crs無法啟動問題分析Oracle
- 前端基礎知識之什麼是節點Node?前端
- Oracle RAC新增節點Oracle
- 知識點
- OpenFeign 使用細節
- 【實用知識】招投標知識兩則
- JS 學習個人知識盲點JS
- 關於range的一個知識點
- Java註解在Android中必須學習的細節知識JavaAndroid
- Flutter 資料庫sqflite使用知識點Flutter資料庫
- laravel-permission使用知識點(慢更)Laravel
- rabbitmq 知識點MQ
- SQL知識點(+)SQL
- 前端知識點前端
- Android 知識點Android
- Weex 知識點
- Promise知識點Promise