Oracle undo我們需要掌握什麼

引言: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段,也就是說它們查詢時可能被阻塞。

UndoRedo區別

好多人尤其是剛剛入門的朋友,總會把這兩個概念搞混淆。

Redo:是基於安全考慮的,會記錄數據庫的所有變化,當數據被誤修改時,使用redo可重新生成,事物重做。是前滾,就是從無到有的新創建。一個新生命的誕生。

Undo:是基於回滾的,當數據被誤修改時,可以從修改的新狀態回退到老狀態,實現事物回滾,相當於撤銷操作。是回滾,就像倒錄音帶一樣,把走過的路反向在走一遍回到原點。

Undo目的:1.事物回滾:rollback   

           2.實例的恢復:掉電,死機,強制關庫,把沒有提交的事物全部回滾

           3.查詢一致性:讀不會被寫所阻塞

Undo段:採用LRU最近最少使用算法來循環覆蓋使用,它的塊有activeinactivenew狀態,如果塊的狀態爲inactive那麼後續的數據就可以覆蓋它了。如果空間不夠Oracle自動分配新空間。

分配:Oracle是按順序循環使用的,不允許跨區覆蓋。

回收:Oracle也是按順序回收的,不允許跨區回收。

RollbackCommit標識位都一樣,都代表事物的結束

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修改爲2A改變產生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修改爲3A改變產生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修改爲2A改變會產生redo記錄,現在A中存放的是2,因此這種回滾操作也會產生redo日誌的,在使用第一個前映像把A中的2修改爲1A改變會產生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

小結:我們要知道rollbackcommit的標識位都一樣都代表事務結束,同時也都會產生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:50select,那麼不管查了多長時間,我們就想要這個時間點的結果,即查什麼時候的就得到什麼時候的結果就叫一致性讀。

一致性讀解決方案:就利用到了回滾段,當我們查詢時發現這個數據塊正在被修改,那麼我們就要跳到回滾段上去讀取它的前映像,結果不會因爲塊修改而改變。這就是著名的“讀不會被寫所阻塞定理”

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 segmentconsistent readcurrent 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

SCN2595945~增長~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回收也是按順序循環回收,不可跨區回收。


undoredoconsistent readcurrent readORA-01555

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章