引言:undo 是Oracle數據庫的重要組件,剛入門的朋友建議要把undo的原理和機制理解明白,尤其是和redo組件的區別和聯繫。瞭解undo就相當於對oracle恢復有了一半的理解。下面我們開始學習Oracle undo需要掌握什麼!
更多的精彩文章請垂詢 www.leonarding.com blog,歡迎大家來探討交流
“分享技術~成就夢想”
一 大話UNDO
Hi 大家好,我是Oracle 的無敵小安[中文名(small undo[英文名),首先我先來介紹一下自己吧,我誕生於一個大家庭Oracle,現在的一把手拉里.埃裏森就是我的締造者,我的誕生可謂Oracle立下了汗馬功勞,使Oracle開拓繮野攻城略地!我的誕生是爲了解決三個大問題,事物回滾,實例恢復,查詢一致性。這樣的功能使Oracle成爲了關係型數據庫中的佼佼者。
例如:讀不會被寫所阻塞,當我們讀取的數據塊正好被他人修改時,我們就可以找到undo段裏保存的前映像來維護一致性。而其他數據庫,sql server mysql等都沒有undo段,也就是說它們查詢時可能被阻塞。
Undo和Redo區別
好多人尤其是剛剛入門的朋友,總會把這兩個概念搞混淆。
Redo:是基於安全考慮的,會記錄數據庫的所有變化,當數據被誤修改時,使用redo可重新生成,事物重做。是前滾,就是從無到有的新創建。一個新生命的誕生。
Undo:是基於回滾的,當數據被誤修改時,可以從修改的新狀態回退到老狀態,實現事物回滾,相當於撤銷操作。是回滾,就像倒錄音帶一樣,把走過的路反向在走一遍回到原點。
Undo目的:1.事物回滾:rollback
2.實例的恢復:掉電,死機,強制關庫,把沒有提交的事物全部回滾
3.查詢一致性:讀不會被寫所阻塞
Undo段:採用LRU最近最少使用算法來循環覆蓋使用,它的塊有active和inactive和new狀態,如果塊的狀態爲inactive那麼後續的數據就可以覆蓋它了。如果空間不夠Oracle自動分配新空間。
分配:Oracle是按順序循環使用的,不允許跨區覆蓋。
回收:Oracle也是按順序回收的,不允許跨區回收。
Rollback和Commit標識位都一樣,都代表事物的結束
Rollback:表示會回滾從上一次提交到現在的所有事物
二 數據庫版本
SYS@LEO1> select * from v$version;
BANNER
--------------------------------------------------------------------------------
Oracle Database 11g Enterprise Edition Release 11.2.0.1.0 - 64bit 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
三 示例演示回滾是否產生REDO日誌。
原理解釋:首先向大家明確一點,redo的產生機理是因爲底層數據塊的變化而產生的,這個數據塊不管是數據文件的還是undo文件的,數據塊的變化都會記錄在redo日誌裏面。
OK我們再來看看undo回滾的原理,假設 一個數據塊命名爲A,這個A裏面存放了數據1,現在我們要把這個1->修改->2->修改->3->修改->2->修改->1 步驟
1.先把1複製一份映像存放到undo塊上用於回滾,這就是前映像,undo塊改變產生redo記錄
2.再把數據塊上的1修改爲2,A改變產生redo記錄,現在A中存放的是2
LEO1@LEO1> drop table leo2 purge; 清理環境
Table dropped.
LEO1@LEO1> create table leo2 as select * from leo1 where rownum<10; 創建leo2表只需要9行記錄即可
LEO1@LEO1> select a.name,b.value from v$statname a,v$mystat b where a.STATISTIC# =b.STATISTIC# and a.NAME='redo size';
NAME VALUE
-----------------------------------------------------------------------------------------------------------------------------------
redo size 2362280
這是初始化的值
LEO1@LEO1> update leo2 set object_id=2 where object_id=1; 1->修改->2
1 row updated.
LEO1@LEO1> select a.name,b.value from v$statname a,v$mystat b where a.STATISTIC# =b.STATISTIC# and a.NAME='redo size';
NAME VALUE
---------------------------------------------------------------------------------------------------------------------------------------------
redo size 2362992
LEO1@LEO1> select 2362992-2362280 from dual; 這是產生的redo量
2362992-2362280
-------------------------
712
3.這時我們又想把2修改爲3,還是先把2複製一份映像存放到undo塊上用於回滾,這就是第二個前映像,undo塊改變產生redo記錄
4.再把A中的2修改爲3,A改變產生redo記錄,現在A中存放的是3
LEO1@LEO1> update leo2 set object_id=3 where object_id=2; 2->修改->3
1 row updated.
LEO1@LEO1> select a.name,b.value from v$statname a,v$mystat b where a.STATISTIC# =b.STATISTIC# and a.NAME='redo size';
NAME VALUE
-------------------------------------------------------------------------------------------------------------------------------------------
redo size 2363348
LEO1@LEO1> select 2363348-2362992 from dual; 這是產生的redo量
2363348-2362992
---------------------------
356
5.我們又想把3回退爲1,那麼就需要先把3回退爲2,由於我們在undo段上記錄了每次改變的前映像,我們直接把前映像回滾回來即可(事物的回滾),使用第二個前映像把A中的3修改爲2,A改變會產生redo記錄,現在A中存放的是2,因此這種回滾操作也會產生redo日誌的,在使用第一個前映像把A中的2修改爲1,A改變會產生redo記錄,現在A中存放的是1,此時rollback操作完畢事務結束。
我們用語句來實現一下上面的操作,rollback特點會回滾從上次提交到現在的所有事務,3->1
LEO1@LEO1> rollback; 3->修改->2->修改->1
Rollback complete.
LEO1@LEO1> select a.name,b.value from v$statname a,v$mystat b where a.STATISTIC# =b.STATISTIC# and a.NAME='redo size';
NAME VALUE
---------------------------------------------------------------------------------------------------------------------------------------
redo size 2364608
LEO1@LEO1> select 2364608-2363348 from dual; 產生的redo量很大的
2364608-2363348
------------------------
1260
小結:我們要知道rollback和commit的標識位都一樣都代表事務結束,同時也都會產生redo日誌。
四 示例演示爲數據庫創建一個新的UNDO表空間。
Oracle 8i 需要手工創建undo表空間
Oracle 9i 自動管理UNDO表空間
Oracle 10g 11g 在創建數據庫時,必須一起創建undo表空間
未來自動化是大趨勢,Oracle 12c會更加的自動化 更加的智能
SYS@LEO1> show parameter undo
NAME TYPE VALUE
------------------------------------ ----------- ------------------------------
undo_management string AUTO Oracle自動管理表空間
undo_retention integer 900 默認保留期15分鐘
undo_tablespace string UNDOTBS1 Oracle默認undo表空間
SYS@LEO1> select tablespace_name,autoextensible from dba_data_files;
TABLESPACE_NAME AUT
-------------------------------------------------------------
USERS YES
UNDOTBS1 YES 這也顯示當前使用的undo表空間
SYSAUX YES
SYSTEM YES
LEO1 NO
Oracle 特點:可以創建好多個undo表空間,但只能使用其中一個,下面我們來創建一個新undo
SYS@LEO1> select tablespace_name,file_name,bytes/1024/1024,autoextensible from dba_data_files;
TABLESPACE_NAME FILE_NAME BYTES/1024/1024 AUT
----------------------------------------------------------------------------------------------------------------------------------------------
USERS /u01/app/oracle/oradata/LEO1/users01.dbf 5 YES
UNDOTBS1 /u01/app/oracle/oradata/LEO1/undotbs01.dbf 165 YES
SYSAUX /u01/app/oracle/oradata/LEO1/sysaux01.dbf 610 YES
SYSTEM /u01/app/oracle/oradata/LEO1/system01.dbf 700 YES
LEO1 /u01/app/oracle/oradata/LEO1/leo1_01.dbf 400 NO
這是所有表空間文件路徑和大小,當前undo表空間165M,我們把新的設置成100M
SYS@LEO1> create undo tablespace undotbs2 datafile '/u01/app/oracle/oradata/LEO1/undotbs02.dbf' size 100M;
Tablespace created.
UNDOTBS2 /u01/app/oracle/oradata/LEO1/undotbs02.dbf 100 NO
爲什麼設置成100M呢,這個大小因人而異,你可以根據自己系統的情況個性化設計滿足需求即可。
SYS@LEO1> alter system set undo_tablespace=undotbs2;如果要使用新的undo表空間,必須切換到該表空間
System altered.
SYS@LEO1> show parameter undo
NAME TYPE VALUE
------------------------------------ ----------- ------------------------------
undo_management string AUTO Oracle自動管理表空間
undo_retention integer 900 默認保留期15分鐘
undo_tablespace string UNDOTBS2 成爲新的默認undo表空間
小結:1.undo是一個很重要的表空間,一個數據庫當且僅當只有一個undo表空間,如果我們想使用新的,必須切換到新表空間。
2.我們不能刪除當前正在使用的undo表空間,如果刪除了數據庫就會crash,如果發現空間太小或者undo段出現壞塊,你必須先創建一個新的,再從老的切換過去。
五 示例分別說明什麼是consistent read和current read?
一致性讀這個話題由來已久,通俗的說一致性讀,就是你什麼時候發出select,不管查詢了多長時間,返回的值都應該是你查詢時刻時間點的。這個話題是由業務特性所引起的,是基於業務需求而來的,Oracle中由SCAN號順序來實現
例如我們查詢一張leo1表
LEO1@LEO1> drop table leo1 purge; 清理環境
Table dropped
LEO1@LEO1> create table leo1 as select * from dba_objects; 創建leo1表
Table created.
LEO1@LEO1> set time on 我們啓動時間點顯示
09:44:38 LEO1@LEO1> set autotrace traceonly
09:44:50 LEO1@LEO1> select * from leo1 where object_type='TABLE'; 我們看一下執行計劃
2818 rows selected.
Execution Plan
----------------------------------------------------------
Plan hash value: 2716644435
--------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
--------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 3003 | 607K| 287 (1)| 00:00:04 |
|* 1 | TABLE ACCESS FULL| LEO1 | 3003 | 607K| 287 (1)| 00:00:04 |
--------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
1 - filter("OBJECT_TYPE"='TABLE')
Note
-----
- dynamic sampling used for this statement (level=2)
Statistics
----------------------------------------------------------
0 recursive calls
0 db block gets
1218 consistent gets
0 physical reads
0 redo size
149962 bytes sent via SQL*Net to client
2580 bytes received via SQL*Net from client
189 SQL*Net roundtrips to/from client
0 sorts (memory)
0 sorts (disk)
2818 rows processed
09:45:04 LEO1@LEO1>
我們從09:44:50發出select語句,到查詢到結果是09:45:04,中間經過了14秒,如果這14秒內有另外的用戶去修改我們查詢的數據塊,那麼得到的結果是修改後的呢,還是修改前的呢,從業務的角度看,我們查詢的是09:44:50的select,那麼不管查了多長時間,我們就想要這個時間點的結果,即查什麼時候的就得到什麼時候的結果就叫一致性讀。
一致性讀解決方案:就利用到了回滾段,當我們查詢時發現這個數據塊正在被修改,那麼我們就要跳到回滾段上去讀取它的前映像,結果不會因爲塊修改而改變。這就是著名的“讀不會被寫所阻塞定理”
Select統計信息裏反應出的一致性讀
0 db block gets 當前讀0個塊
1218 consistent gets 一致性讀1218個塊
當前讀,什麼是當前讀,當前讀就是我們操作的數據塊一定是最當前的狀態,這就會產生鎖,防止多個會話同時操作一個數據塊,誰先獲得塊誰就擁有修改權,其他會話就要等待。
例如我們修改一張leo1表
10:13:24 LEO1@LEO1> update leo1 set object_type='leonarding' where object_type='TABLE';
2818 rows updated.
Execution Plan
----------------------------------------------------------
Plan hash value: 3524047104
---------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
---------------------------------------------------------------------------
| 0 | UPDATE STATEMENT | | 12 | 132 | 287 (1)| 00:00:04 |
| 1 | UPDATE | LEO1 | | | | |
|* 2 | TABLE ACCESS FULL| LEO1 | 12 | 132 | 287 (1)| 00:00:04 |
---------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
2 - filter("OBJECT_TYPE"='TABLE')
Note
-----
- dynamic sampling used for this statement (level=2)
Statistics
----------------------------------------------------------
285 recursive calls
12121 db block gets
3126 consistent gets
13 physical reads
2273320 redo size
837 bytes sent via SQL*Net to client
825 bytes received via SQL*Net from client
3 SQL*Net roundtrips to/from client
6 sorts (memory)
0 sorts (disk)
2818 rows processed
10:13:27 LEO1@LEO1>
我們從10:13:24發出update語句,到修改完是10:13:27,中間經過了3秒,這期間都是鎖定狀態,別的會話必須等待,如果修改完後我一直不提交,這些數據塊就都是鎖定的,此時如果有其他會話來查詢這些數據塊的內容,那麼就要從回滾段裏讀取前映像數據了,不允許直接查看數據塊當前的狀態,因爲新修改的數據還沒有提交,事物還沒有結束。
update統計信息裏反應出的當前讀
12121 db block gets 當前讀12121個塊,說明鎖定了這麼多個塊
3126 consistent gets 一致性讀相對就少很多
小結:這就是undo segment在consistent read和current read方式下的運作機制。
六 示例演示一個導致ora-01555錯誤的場景
Ora-01555經典中的經典,直到現在Oracle還是沒有完美解決,既然oracle使用到了回滾段來保證讀一致性,有利必有弊,那麼就會有快照太舊的情況發生,我們所查詢的前映像被無情的覆蓋了。
原因:就是當一個select查詢等待時間太長,這個過程中查詢的數據被修改完畢commit了,此時它的前映像就爲inactive狀態,如果這時候有其他會話也想用undo段,就會把inactive狀態的回滾區覆蓋掉,這時正好select查詢完畢想讀取它的前映像,發現已經被覆蓋了,此時oracle會報ora-01555錯誤。理解原理很重要
我用一種叫做閃回查詢的功能來實現ora-01555
flashback query:原理也是使用undo segment中的前映像,來查詢出我們修改之前的數據映像,一般用於誤操作恢復。可以基於時間閃回還可以基於SCN號閃回,一秒鐘包括好幾個SCN號,不管基於什麼oracle內部都是按照SCN號順序操作的。只要我們查找的原映像被覆蓋了,就會報ora-01555錯誤。
實驗
LEO1@LEO1> drop table leo3 purge; 清理環境
Table dropped.
LEO1@LEO1> create table leo3 (name varchar2(20),sale number); 創建leo3
Table created.
這個表裏記錄下面4位小朋友去米國遊玩的旅行費
LEO1@LEO1> insert into leo3 values('leonarding',10000);
1 row created.
LEO1@LEO1> insert into leo3 values('sunev_yu',20000);
1 row created.
LEO1@LEO1> insert into leo3 values('alan',30000);
1 row created.
LEO1@LEO1> insert into leo3 values('tigerfish',40000);
1 row created.
LEO1@LEO1> commit;
Commit complete.
看樣子tiger最有錢了,跟着tiger走吃喝全都有!
LEO1@LEO1> select * from leo3;
NAME SALE
----------------------------------------------------------
leonarding 10000
sunev_yu 20000
alan 30000
tigerfish 40000
先查一下還沒有ship之前的SCN號備用
LEO1@LEO1> select dbms_flashback.get_system_change_number scn from dual;
SCN
-----------------
2595945
由於tiger的慷慨大方包吃包住,我們玩耍的很盡興!在結束旅行之前大家去銀行查一查每個人的cash book
LEO1@LEO1> update leo3 set sale=100 where name='tigerfish';
1 row updated.
LEO1@LEO1> update leo3 set sale=200 where name='alan';
1 row updated.
LEO1@LEO1> update leo3 set sale=300 where name='sunev_yu';
1 row updated.
LEO1@LEO1> update leo3 set sale=400 where name='leonarding';
1 row updated.
LEO1@LEO1> commit;
Commit complete.
哈 發現一個規律現金越少蹭功越強,在此向tigerfish alan兩位大師表達敬意!歐巴~阿加西~
LEO1@LEO1> select * from leo3;
NAME SALE
----------------------------------------------------------
leonarding 400
sunev_yu 300
alan 200
tigerfish 100
LEO1@LEO1> select dbms_flashback.get_system_change_number scn from dual;
SCN
----------------
2605429
看SCN:2595945~增長~2605429,記錄着數據庫所有變化,是持續增長的
這時他們都想看看來時每個人的現金額是多少,花了多少,回去好交差
LEO1@LEO1> select name,sale from leo3 as of scn 2595945;
NAME SALE
----------------------------------------------------------
leonarding 10000
sunev_yu 20000
alan 30000
tigerfish 40000
這些數據,就是從undo segment中讀取出來的,我們需要指定當時的SCN號,我們要根據SCN號進行閃回查詢。
LEO1@LEO1> alter database datafile '/u01/app/oracle/oradata/LEO1/undotbs02.dbf' autoextend off;
Database altered.
禁用undo表空間自動擴展功能
LEO1@LEO1> select tablespace_name,file_name,autoextensible from dba_data_files where tablespace_name='UNDOTBS2';
TABLESPACE_NAME FILE_NAME AUT
----------------------------------------------------------------------------------------------------------------------------------------------
UNDOTBS2 /u01/app/oracle/oradata/LEO1/undotbs02.dbf NO
我們現在開始覆蓋undo segment中的inactive區中前映像,覆蓋之後我們再查詢時就會出現ora-01555
LEO1@LEO1> begin
for i in 1..100000 loop
update leo3 set sale=10000 where sale>=100;
rollback;
end loop;
end;
/
2 3 4 5 6 7
PL/SQL procedure successfully completed.
經典中的經典出現了ORA-01555
LEO1@LEO1> select name,sale from leo3 as of scn 2595945;
select name,sale from leo3 as of scn 2595945
*
ERROR at line 1:
ORA-01555: snapshot too old: rollback segment number 12 with name "_SYSSMU12_3331027169$" too small
ORA-01555:快照太舊:名爲_SYSSMU12_3331027169$的12號回滾段太小,哈~我們成功模擬出undo segment被覆蓋場景。
小結:上述實驗說明了undo分配是按順序循環使用,不可跨區覆蓋,undo回收也是按順序循環回收,不可跨區回收。
undo,redo,consistent read,current read,ORA-01555