| 前言 首先思考一個問題:如果對數(shù)據(jù)庫進行了多次修改,如果前面的修改成功,后面修改時發(fā)生了意外,怎么辦? 案例:以銀行轉(zhuǎn)賬為例。 有帳戶表記錄如下: 帳號 姓名 余額 1 張三 2000 2 李四 1000 假設(shè)張三給李四轉(zhuǎn)賬300塊,則對數(shù)據(jù)庫的修改必然有兩步: 第一步:減少張三的余額:2000→1700 第二步:增加李四的余額:1000→1300 但是如果第一步完成以后,馬上就死機(或者斷電、地震……)了。 等數(shù)據(jù)庫重啟以后,目前的帳戶情況變?yōu)?#xff1a; 帳號 姓名 余額 1 張三 1700 2 李四 1000 這表示,張三的余額減少了,但是李四并沒有收到轉(zhuǎn)賬的錢。 說明: ?在實際的銀行項目中,轉(zhuǎn)賬不會是只有一個帳戶表,直接進行余額修改。在此簡化的目的是為了便于說明事務(wù)的相關(guān)概念。 思考:數(shù)據(jù)庫如何操作才能避免這種情況發(fā)生呢? 要解決上面的問題,必須學習一個新的知識點:事務(wù),這是數(shù)據(jù)庫中非常重要的概念,必須掌握! 問題:為什么要事務(wù)? 回答: 在對數(shù)據(jù)進行多次增刪改以后,如果要保證所有的操作同時成功,或者同時失敗。必須使用事務(wù)。 問題:什么是事務(wù)? 回答: 事務(wù)就是業(yè)務(wù)上的一個邏輯工作單元,它能夠保證其中對數(shù)據(jù)所有的操作,要么全部成功,要么失敗。 例如在轉(zhuǎn)賬時,一個帳戶要增加余額,一個帳戶要減少余額,這兩個操作在業(yè)務(wù)上必須當一個整體,也叫做“一個邏輯工作單元”。 “符合邏輯”就是指滿足符合業(yè)務(wù)要求。 問題:事務(wù)的原理是什么? 回答: 主要依賴于日志。如果事務(wù)沒有完成,則日志中沒有結(jié)束標記,數(shù)據(jù)庫就會執(zhí)行前面各步的反向操作,例如: 1、事務(wù)開始→張三減300(2000→1700)→死機。 2、數(shù)據(jù)庫重啟→讀取日志,檢查事務(wù)沒有結(jié)束標記,執(zhí)行“反向”操作→張三加300(1700→2000)→張三數(shù)據(jù)恢復了。 說明:數(shù)據(jù)庫事務(wù)恢復的過程其實是很很復雜的,在此只是講解最基本的原理。 問題:事務(wù)有哪些特征? 回答: 一共有4個: ?原子性:以轉(zhuǎn)賬為例。帳戶減少和帳戶增加是兩個DML語句,但是被當一個“原子”,所以外部程序是不知道這個原子中有幾個SQL的。更不能只執(zhí)行其中一個SQL,只能同時成功,或者同時失敗。即:無法拆出事務(wù)中某一條SQL語句來單獨執(zhí)行。 ?一致性:要么都是改變前的改變,要么都是改變后的狀態(tài)。 例如:轉(zhuǎn)賬前分別是1000和2000,總和3000;轉(zhuǎn)賬300后分別是1300和1700,總和也是3000。這就叫做一致性。不一致就是,轉(zhuǎn)賬后分別1300和2000(不變),總是3300,一個是改變后的狀態(tài),另一個還是改變前的狀態(tài)。 ?隔離性:在某個時間段,肯定有很多人都在轉(zhuǎn)賬,每個人的轉(zhuǎn)賬都是在一個自己的事務(wù)中,所以在一個數(shù)據(jù)庫中,有很多事務(wù)會同時存在。雖然同時存在很多事務(wù),但是事務(wù)之間不會相互影響。 ?持久性:如果事務(wù)提交成功,則數(shù)據(jù)修改永遠生效;如果是回滾,則數(shù)據(jù)完全沒有被修改,就相當于沒有這件事情發(fā)生。事務(wù)結(jié)束以后,必須重新啟動一個新的事務(wù)才能修改數(shù)據(jù)。 說明:可以用口訣(一原永隔)來幫助記憶。 問題:如何使用事務(wù)? 回答:一共有三步。 1、先開啟事務(wù)。 ?在Oracle中,事務(wù)是在上一次事務(wù)結(jié)束以后,數(shù)據(jù)“第一次”被修改時自動開啟。 ?在java中,設(shè)置連接為手動提交模式開始。代碼: connection.setAutoCommit(false)。 2、進行(多次)數(shù)據(jù)操作(增刪改)…… 3、結(jié)束事務(wù): commit、rollback。 問題:如何結(jié)束事務(wù)? 回答: 有兩種方法: 1、確認對數(shù)據(jù)的修改:提交,commit。 2、撤消對數(shù)據(jù)的修改:回滾,rollback。 另外,當Oracle系統(tǒng)或者PL/SQL腳本拋出異常,都會造成數(shù)據(jù)回滾。 問題:能不能做局部提交或者局部回滾? 如執(zhí)行了三個insert語句,能不能只提交或者回滾前面2個。 回答: 如果說要提交,只能提交所有的sql語句。 如果說要回滾,可以利用“事務(wù)保存點”來做局部回滾。注意,此時事務(wù)并沒有結(jié)束。 示例: 執(zhí)行: 1、 insert into dept values(50,'a',null); 2、 insert into dept values(60,'b',null); 3、 savepoint a; 4、 insert into dept values(70,'c',null); 5、 rollback to savepoint a; 問題:現(xiàn)在插入了幾個新部門? 分析:回滾到保存點“a”,表示保存點以后的所有數(shù)據(jù)操作取消。所以只插入了兩個部門。 問題:現(xiàn)在的事務(wù)結(jié)束了嗎? 回答:沒有,必須再執(zhí)行commit或者rollback來結(jié)束事務(wù)。 說明:在工作中這種“局部回滾”用得比較少。 示例:異常與事務(wù)。 問題:假設(shè)現(xiàn)在有50和60兩個新部門。執(zhí)行下面代碼以后,這兩個部門刪除了沒有? begin delete from dept where deptno=50; delete from dept where deptno=60; raise_application_error(-20000,'拋個異常玩'); end; 分析: 沒有。因為拋出異常以后,數(shù)據(jù)庫會自動回滾。 問題:現(xiàn)在再執(zhí)行commit,是否刪除了? 回答: 還是沒有。因為事務(wù)在回滾以后,事務(wù)結(jié)束,這表示剛才執(zhí)行的sql語句完全不存在了,所以如果還要刪除,則必須重新執(zhí)行sql語句。 即:在事務(wù)結(jié)束以后,再執(zhí)行commit和rollback是沒有用的。 問題:什么是提交模式,與事務(wù)有什么關(guān)系? 回答: 提交模式有兩種: 自動提交:在每一次數(shù)據(jù)被修改以后,自動提交。這種模式下是無法支持事務(wù)的。 手動提交:所有的數(shù)據(jù)修改都在內(nèi)存中,當執(zhí)行commit時才一起提交。只要有一條sql執(zhí)行失敗,則所有的sql自動回滾。只能在手動提交模式下才能使用事務(wù)。 問題:事務(wù)有哪幾種隔離級別? 回答: 事務(wù)隔離級別在理論上有4種,從低到高分別是: ?未提交讀:一個事務(wù)可以讀到其它事務(wù)未提交的數(shù)據(jù)。 ?提交讀:一個事務(wù)只能讀到其它事務(wù)已提交的數(shù)據(jù)。 ?可重復讀:一個事務(wù)中,不管數(shù)據(jù)有沒有被其它事務(wù)所修改,讀到的數(shù)據(jù)都是不變的。 ?串行讀:一個事務(wù)要操作數(shù)據(jù),必須要等到其它事務(wù)結(jié)束才能訪問,包括查詢。 Oracle只支持其中兩種: 提交讀和串行讀,默認就是提交讀。 問題:數(shù)據(jù)庫為什么要在理論上分這4種隔離級別,通常用哪一種呢? 回答: 事務(wù)隔離級別越高,則并發(fā)性越低,即能夠同時使用數(shù)據(jù)庫的人就越少。 雖然未提交讀性能最高,但是會造成數(shù)據(jù)不一致(這叫:臟讀)。而串行讀能最大限度的保護數(shù)據(jù),但性能又太低。 所以通常要在性能和安全之間做一個選擇,在Oracle中,就使用默認的隔離級別:提交讀。 原文地址:http://hi.baidu.com/xydba/blog/item/b64894fe5ec34b076d22eb11.html |