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