數據庫事務的解讀

今天晚上我們就來談談事務,事務是保證數據庫四大特性的前提,有事務纔能有效防止髒讀、幻讀、不可重複讀

爲了方便演示事務,我們需要創建一個account 賬戶表:

<span style="font-family:SimSun;">CREATE TABLE account(
	id NUMBER PRIMARY KEY,
	NAME VARCHAR(30),
	balance NUMBER (10,2)
);

INSERT INTO account VALUES(1,'zs', 100000);
INSERT INTO account VALUES(2,'ls', 100000);
INSERT INTO account VALUES(3,'ww', 100000);

SELECT * FROM account;</span>

1、什麼是事務?

我們這裏舉一個簡單通俗的例子吧。

銀行轉賬!張三轉10000塊到李四的賬戶,這其實需要兩條SQL語句:

第一條SQL語句:給張三的賬戶減去10000元;

第二條SQL語句: 給李四的賬戶加上10000元。

如果在第一條SQL語句執行成功後,在執行第二條SQL語句之前,程序被中斷了(可能是拋出了某個異常,也可能是其他什麼原因),那麼李四的賬戶沒有加上10000元,而張三卻減去了10000元。這肯定是不行的!

你現在可能已經知道什麼是事務了吧!事務中的多個操作,要麼完全成功,要麼完全失敗!不可能存在成功一半的情況!也就是說給張三的賬戶減去10000元如果成功了,那麼給李四的賬戶加上10000元的操作也必須是成功的;否則給張三減去10000元,以及給李四加上10000元都是失敗的!

2、事務的四大特性(ACID)

事務的四大特性是:

 原子性(Atomicity):事務中所有操作是不可再分割的原子單位。事務中所有操作要麼全部執行成功,要麼全部執行失敗。

 一致性(Consistency):事務執行後,數據庫狀態與其它業務規則保持一致。如轉賬業務,無論事務執行成功與否,參與轉賬的兩個賬號餘額之和應該是不變的。

 隔離性(Isolation):隔離性是指在併發操作中,不同事務之間應該隔離開來,使每個併發中的事務不會相互干擾。

 持久性(Durability):一旦事務提交成功,事務中所有的數據操作都必須被持久化到數據庫中,即使提交事務後,數據庫馬上崩潰,在數據庫重啓時,也必須能保證通過某種機制恢復數據。

這個在面試的時候經常會被問到,所以想應付面試問得事務就得了解這四大特性。


3、JDBC事務

在jdbc中處理事務,都是通過Connection完成的!

同一事務中所有的操作,都必須使用同一個Connection對象!

1、JDBC中的事務

Connection的三個方法與事務相關:

1、setAutoCommit(boolean):設置是否爲自動提交事務,如果true(默認值就是true)表示自動提交,也就是每條執行的SQL語句都是一個單獨的事務,如果設置false,那麼就相當於開啓了事務了;con.setAutoCommit(false)表示開啓事務!!!

2、commit():提交結束事務;con.commit();表示提交事務

3、rollback():回滾結束事務。con.rollback();表示回滾事務

 

jdbc處理事務的代碼格式:

try {

  con.setAutoCommit(false);//開啓事務…

  ….

  …

  con.commit();//try的最後提交事務

} catch() {

  con.rollback();//回滾事務

}

下面我們寫一個java的例子
<span style="font-family:SimSun;">/**
	 * 轉賬方法
	 * @param from 從某賬戶
	 * @param to	轉到某賬戶
	 * @param money 轉賬金額
	 */
	public void transferAcc(String from, String to, double money) {
		// 對事務的操作必須使用Connection對象!
		Connection con = null;
		try {
			con = JdbcUtils.getConnection();
			//開啓事務
			con.setAutoCommit(false);
			AccountDao dao = new AccountDao();
			int i = dao.updateBalance(con, from, -money);//給from減去相應金額
			if(i>0){
				System.out.println("說明轉出成功.但此時還沒開始轉入!");
				throw new RuntimeException("不好意思,停電!檢查數據是否已經回滾");
			}

			int ii = dao.updateBalance(con, to, money);//給to加上相應金額
			if(ii>0){
				System.out.println("說明轉入成功");
			}
			//提交事務
			con.commit();
			con.close();
		} catch(Exception e) {
			//回滾事務
			try {
				con.rollback();
				con.close();
			} catch (SQLException e1) {
			}
			throw new RuntimeException(e);
		}
	}			</span>

其中JdbcUtils是我的dao工具類,爲了避免本文的臃腫,稍後我會在另一篇博文寫這個類。

我們可以自己寫一個類然後調用這個例子測試一下事務。簡單來說如果只要有一條sql沒成功事務就會回滾。

4、事務隔離級別

 1、 併發事務問題的發生

要了解事物的隔離級別,就要先了解事務併發會引起的問題,爲什麼需要隔離,因爲併發事務導致的問題大致有5類,其中兩類是更新問題,三類是讀問題。

1、 髒讀(dirty read):讀到另一個事務的未提交更新數據,即讀取到了髒數據;

2、不可重複讀(unrepeatable read):對同一記錄的兩次讀取不一致,因爲另一事務對該記錄做了修改;

3、 幻讀(虛讀)(phantom read):對同一張表的兩次查詢不一致,因爲另一事務插入了一條記錄;


下面簡單列舉一下這些三類問題發生的場景:

髒讀

事務1:張三給李四轉賬100元

事務2:李四查看自己的賬戶

 t1:事務1:開始事務

 t2:事務1:張三給李四轉賬100元

 t3:事務2:開始事務

 t4:事務2:李四查看自己的賬戶,看到賬戶多出100元(髒讀)

 t5:事務2:提交事務

 t6:事務1:回滾事務,回到轉賬之前的狀態


不可重複讀

事務1:酒店查看兩次1048號房間狀態

事務2:預訂1048號房間

 t1:事務1:開始事務

 t2:事務1:查看1048號房間狀態爲空閒

 t3:事務2:開始事務

 t4:事務2:預定1048號房間

 t5:事務2:提交事務

 t6:事務1:再次查看1048號房間狀態爲使用

 t7:事務1:提交事務

對同一記錄的兩次查詢結果不一致!


幻讀

事務1:對酒店房間預訂記錄兩次統計

事務2:添加一條預訂房間記錄

 t1:事務1:開始事務

 t2:事務1:統計預訂記錄100條

 t3:事務2:開始事務

 t4:事務2:添加一條預訂房間記錄

 t5:事務2:提交事務

 t6:事務1:再次統計預訂記錄爲101記錄

 t7:事務1:提交

  對同一表的兩次查詢不一致!

 

不可重複讀和幻讀的區別:

 1、不可重複讀是讀取到了另一事務的更新;

 2、幻讀是讀取到了另一事務的插入(MySQL中無法測試到幻讀);


 爲了防止出現上述這些情況就有了隔離級別。

 2、四大隔離級別

4個等級的事務隔離級別,在相同數據環境下,使用相同的輸入,執行相同的工作,根據不同的隔離級別,可以導致不同的結果。不同事務隔離級別能夠解決的數據併發問題的能力是不同的。


SERIALIZABLE

 不會出現任何併發問題,因爲它是對同一數據的訪問是串行的,非併發訪問的,性能最差;


REPEATABLE READ(可重複讀)(MySQL)

  防止髒讀和不可重複讀,不能處理幻讀問題,性能比SERIALIZABLE好。


READ COMMITTED(讀已提交數據)(Oracle)

  防止髒讀,沒有處理不可重複讀,也沒有處理幻讀,性能比REPEATABLE READ好。


READ UNCOMMITTED(讀未提交數據)

  啥也不處理!可能出現任何事務併發問題性能最好

 

MySQL的默認隔離級別爲REPEATABLE READ

Oracle 默認隔離級別爲READ COMMITTED

3、如何在JDBC設置隔離級別?

con.setTransactionIsolation(int level)

參數可選值如下:

 Connection.TRANSACTION_READ_UNCOMMITTED;

 Connection.TRANSACTION_READ_COMMITTED;

 Connection.TRANSACTION_REPEATABLE_READ;

 Connection.TRANSACTION_SERIALIZABLE。

 

事務總結:

 1、事務的特性:ACID;

 2、事務開始邊界與結束邊界:開始邊界(con.setAutoCommit(false)),結束邊界(con.commit()或con.rollback());

 3、事務的隔離級別:READ_UNCOMMITTEDREAD_COMMITTEDREPEATABLE_READSERIALIZABLE多個事務併發執行時才需要考慮併發事務。




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