事務-學習筆記

一、事務(Transaction)概述

  • 其實指的一組操作,裏面包含許多個單一的邏輯。只要有一個邏輯沒有執行成功,那麼都算失敗。 所有的數據都回歸到最初的狀態(回滾)。
  • 爲什麼要有事務?
    爲了確保邏輯的成功。 如: 銀行轉賬。

二、演示事務

  • 命令行方式:
開啓事務:
	start transaction;
提交:
	commit; 提交事務, 數據將會寫到磁盤上的數據庫
回滾:
	rollback ;  數據回滾,回到最初的狀態。
  • 代碼裏面的事務,主要是針對連接來的。
 - 通過conn.setAutoCommit(false )來關閉自動提交的設置。
 - 提交事務  conn.commit();
 - 回滾事務 conn.rollback();
@Test
public void testTransaction(){
	Connection conn = null;
	PreparedStatement ps = null;
	ResultSet rs = null;
	try {
		conn = JDBCUtil.getConn();
		
		//連接,事務默認就是自動提交的。 關閉自動提交。
		conn.setAutoCommit(false);
			
		String sql = "update account set money = money - ? where id = ?";
		ps = conn.prepareStatement(sql);
			
		//扣錢, 扣ID爲1 的100塊錢
		ps.setInt(1, 100);
		ps.setInt(2, 1);
		ps.executeUpdate();
			
		int a = 10 /0 ;//有了異常,下面代碼就不會執行了
			
		//加錢, 給ID爲2 加100塊錢
		ps.setInt(1, -100);
		ps.setInt(2, 2);
		ps.executeUpdate();
			
		//成功: 提交事務。
		conn.commit();
			
	} catch (SQLException e) {
		try {
			//失敗: 回滾事務
			conn.rollback();
		} catch (SQLException e1) {
			e1.printStackTrace();
		}
		e.printStackTrace();
			
	}finally {
		JDBCUtil.release(conn, ps, rs);
	}
}

三、事務特性ACID(面試)

  • 原子性(Atom)
    指的是 事務中包含的邏輯,不可分割。
  • 一致性(Consistent)
    指的是 事務執行前後,數據完整性。
  • 隔離性(Isolate)
    指的是 事務在執行期間不應該受到其他事務的影響。
  • 持久性(Durable)
    指的是 事務執行成功,那麼數據應該持久保存到磁盤上。

四、安全問題&隔離級別(面試)

  • 不考慮隔離級別設置,那麼會出現以下問題。

讀 問題

  • 髒讀
    一個事務讀到另外一個事務還未提交的數據。
  • 不可重複讀
    一個事務讀到了另外一個事務提交的數據 ,造成了前後兩次查詢結果不一致。
  • 幻讀
    一個事務讀到了另一個事務已提交的插入的數據,導致多次查詢結果不一致。

寫 問題

  • 丟失更新:
    B事務如果提交,會造成A事務的操作無效。
    B事務回滾,也會造成A事務更新失效。
  • 解決方法:悲觀鎖,樂觀鎖
  1. 悲觀鎖
    指事務在一開始就認爲丟失更新一定會發生, 這是一件很悲觀的事情。 具體操作步驟如下:
3. 所以事務在執行操作前,先查詢一次數據, 查詢語句如下:
	select * from student  for update  ;           
	後面的for update 其實是數據庫鎖機制 、 一種排他鎖。
4. 哪個事務先執行這個語句, 哪個事務就持有了這把鎖, 可以查詢出來數據, 後面的事務想再執行這條語句,不會有任何數據顯示,就只能等着。 
5. 一直等到前面的那個事務提交數據後, 後面的事務數據才會出來,那麼纔可以往下接着操作。 
補充:就像排隊上廁所一樣,只有裏面的人出來了,才能進去。 這其實就是 java 中的同步的概念。
  1. 樂觀鎖
    樂觀鎖是指,從來不會覺得丟失更新會發生。要求程序員在數據庫中添加字段,然後在後續更新的時候,對該字段進行判定比對, 如果一致才允許更新。
例:
1. 數據庫表中,額外添加了一個version字段, 用於記錄版本, 默認從0 開始, 只要有針對表中數據進行修改的,那麼version就+1.
2. 開啓A事務, 然後開啓B事務 。 
3. A 先執行數據庫表操作。 因爲以前都沒有人修改過。 所以是允許A事務修改數據庫的,但是修改完畢,就把version的值變成  1 了 。
4. B事務, 這時候如果想執行修改,那麼是不允許修改的。 因爲B事務以前是沒有查詢過數據庫內容的,所以它認爲數據庫版本還是0 。 但是數據庫的版本經過A修改,已經是1了。
所以這時候不允許修改, 要求其重新查詢 。
5. B重新查詢後, 將會得到version 爲 1的數據,這份數據就是之前A 事務修改的數據了, B 在進行修改,也是在A的基礎上修改的。 所以就不會有丟失更新的情況出現了。

補充:樂觀鎖的機制 ,其實是通過比對版本或者比對字段的方式來實現的,與版本控制軟件【SVN , GIT】機制是一樣的。

隔離級別

- 按效率劃分,從高到低
	讀未提交  > 讀已提交  > 可重複讀  > 可串行化

- 按攔截程度 ,從高到底
	可串行化 > 可重複讀  > 讀已提交  >  讀未提交
  • Read Uncommited【讀未提交】
    指的是 : 一個事務可以讀取到另一個事務還未提交的數據。 這就會引發 “髒讀” 讀取到的是數據庫內存中的數據,而並非真正磁盤上的數據。
例子: 
1. 開啓一個命令行窗口A, 開始事務,然後查詢表中記錄。  
設置當前窗口的事務隔離級別爲:讀未提交  命令如下:
	set session transaction isolation level read uncommitted;
2. 另外在打開一個窗口B, 也開啓事務, 然後執行 sql 語句, 但是不提交
3. 在A窗口重新執行查詢, 會看到B窗口沒有提交的數據。 
  • Read Commited 【讀已提交】
    與前面的讀未提交剛好相反,這個隔離級別是 ,只能讀取到其他事務已經提交的數據,那些沒有提交的數據是讀不出來的。屏蔽了髒讀的情況,但是這會造成一個問題是: 前後讀取到的結果不一樣。 發生了不可重複!!!, 所謂的不可重複讀,就是不能執行多次讀取,否則出現結果不一樣。
例子:
1. 開啓一個命令行窗口A, 開始事務,然後查詢表中記錄。  
 設置當前窗口的事務隔離級別爲:讀已提交  命令如下:
	set session transaction isolation level read committed;
2. 另外在打開一個窗口B, 也開啓事務, 然後執行 sql 語句, 但是不提交 
3. 在A窗口重新執行查詢, 是不會看到B窗口剛纔執行sql 語句的結果,因爲它還沒有提交。
4. 在B窗口執行提交。
5. 在A窗口中執行查看, 這時候纔會看到B窗口已經修改的結果。
6. 但是這會造成一個問題是: 在A窗口中, 第一次查看數據和第二次查看數據,結果不一樣。
  • Repeatable Read 【重複讀】
    MySql 默認的隔離級別就是這個。該隔離級別, 可以讓事務在自己的會話中重複讀取數據,並且不會出現結果不一樣的狀況,即使其他事務已經提交了,也依然還是顯示以前的數據。(讀到的不是最新更新的數據,確保本事務不受其他事務影響)
例子:
1. 開啓一個命令行窗口A, 開始事務,然後查詢表中記錄。  
2. 設置當前窗口的事務隔離級別爲:重複讀  命令如下:
	set session transaction isolation level repeatable read;
3. 另外在打開一個窗口B, 也開啓事務, 然後執行 sql 語句, 但是不提交 
4. 在A窗口重新執行查詢, 是不會看到B窗口剛纔執行sql 語句的結果,因爲它還沒有提交。
5. 在B窗口執行提交。
6. 在A窗口中執行查看, 這時候查詢結果,和以前的查詢結果一致。不會發生改變。
  • Serializable 【可串行化(序列化)】
    該事務級別是最高級的事務級別了,如果有一個連接設置隔離級別爲可串行化,那麼誰先打開事務,誰就有了先執行的權利,誰後打開事務,就只能等着,等前面的那個事務,提交或者回滾後纔會執行。這種隔離級別比前面幾種都要強大一點,也就是前面幾種的問題【髒讀、不可重複讀、幻讀】都能夠解決。但是都使用該隔離級別也會有些問題。 比如造成併發的性能問題。 其他的事務必須得等當前正在操作表的事務先提交,才能接着往下,否則只能一直在等着。所以比較少用,容易造成性能上的問題,效率比較低。
例子:
1. 開啓一個命令行窗口A, 開始事務,然後查詢表中記錄。  
2. 設置當前窗口的事務隔離級別爲:serializable 命令如下:
	set session transaction isolation level read serializable;
3. 另外在打開一個窗口B, 也開啓事務, 然後執行 sql 語句, 但是不提交 
4. 在A窗口重新執行查詢, 會卡主,沒有任何信息顯示。 
5. 在B窗口執行提交。
6. 在A窗口中執行查看, 這時候纔會顯示結果。

五、總結

  1. 在代碼裏面使用事務
	conn.setAutoCommit(false);
	conn.commit();
	conn.rollback();
  1. 事務只是針對連接連接對象,如果再開一個連接對象,那麼那是默認的提交。
  2. 事務是會自動提交的。
  3. 安全隱患:
讀
	髒讀:一個事務讀到了另一個事務未提交的數據。
	不可重複讀:一個事務讀到了另一個事務已提交的數據,造成前後兩次查詢結果不一致。
	幻讀:一個事務讀到了另一個事務insert的數據 ,造成前後查詢結果不一致 。
寫
	丟失更新。
  1. 隔離級別
讀未提交
> 引發問題: 髒讀 
讀已提交
> 解決: 髒讀 , 引發: 不可重複讀
可重複讀
> 解決: 髒讀 、 不可重複讀 , 未解決: 幻讀
可串行化
> 解決: 髒讀、 不可重複讀 、 幻讀。  導致:性能下降
  1. 補充:
mySql 默認的隔離級別是 可重複讀
Oracle 默認的隔離級別是  讀已提交
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章