數據庫可不僅僅只有CRUD!關於事務瞭解多少!


關於MySQL的基礎可以看我的上一篇博客:初學者不點進去就太虧了,MYSQL數據庫超全知識點總結!

1 事務

Transaction其實指的一組操作,裏面包含許多個單一的邏輯。只要有一個邏輯沒有執行成功,那麼都算失敗。所有的數據都回歸到最初的狀態(回滾)

  • 爲什麼要有事務?

爲了確保邏輯的成功。例子:銀行的轉賬。

1.1 命令行演示

  1. 開啓事務

    start transaction;
    
  2. 提交或者回滾事務

    commit; -- 提交事務,數據將會寫到磁盤上的數據庫
    rollback; -- 數據回滾,回到最初的狀態。
    
  3. 關閉自動提交功能
    在這裏插入圖片描述

  4. 演示事務
    [外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-I6UQqpaE-1586240624477)(img/img02.png)]

1.2 代碼演示

代碼裏面的事務,只是針對連接連接對象,如果再開一個連接對象,那麼那是默認的提交。

  1. 通過conn.setAutoCommit(false);來關閉自動提交的設置。
  2. 提交事務conn.commit();
  3. 回滾事務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);
    	}
    }
    

1.3 事務的特性

  • 原子性: 事務中包含的邏輯,不可分割。

  • 一致性: 事務執行前後。數據完整性

  • 隔離性: 事務在執行期間不應該受到其他事務的影響

  • 持久性: 事務執行成功,那麼數據應該持久保存到磁盤上

1.4 事務的安全隱患

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

1.4.1 讀

髒讀、不可重讀讀、幻讀.

  1. 髒讀: 一個事務讀到另外一個事務還未提交的數據
  2. 不可重複讀: 一個事務讀到了另外一個事務提交的數據 ,造成了前後兩次查詢結果不一致。
  3. 幻讀: 一個事務讀到了另一個事務insert的數據,造成前後查詢結果不一致

1.4.2 寫

丟失更新

  1. 丟失更新: 如果多個線程操作,基於同一個查詢結構對錶中的記錄進行修改,那麼後修改的記錄將會覆蓋前面修改的記錄,前面的修改就丟失掉了。

1.5 隔離級別

  1. 讀未提交(Read uncommitted)

    引發問題:髒讀

  2. 讀已提交(Read committed)

    解決:髒讀,引發:不可重複讀

  3. 可重複讀(Repeatable read)

    解決:髒讀、不可重複讀,未解決:幻讀

  4. 可串行化(Serializable)

    解決:髒讀、不可重複讀、幻讀

  • 按效率劃分,從高到低

    讀未提交 > 讀已提交 > 可重複讀 > 可串行化

  • 按攔截程度 ,從高到底

    可串行化 > 可重複讀 > 讀已提交 > 讀未提交

  • MySQL 默認的隔離級別是: 可重複讀

  • Oracle 默認的隔離級別是: 讀已提交

√:可能出現  ×:不會出現 髒讀 不可重複讀 幻讀
Read uncommitted
Read committed ×
Repeatable read × ×
Serializable × × ×

1.5.1 讀未提交

  1. 查看事務的隔離級別
    -- 我安裝的是MySQL8,原先的select @@tx_isolation;不能用了
    select @@transaction_isolation;
    
  2. 設置隔離級別爲讀未提交
    -- read uncommitted 讀未提交
    set session transaction isolation level read uncommitted;
    
  3. 引發的問題:兩個併發的事務,“事務A:更新”、“事務B:查詢”,事務B讀取了事務A尚未提交的數據,即我們所說的髒讀。

1.5.2 讀已提交

  1. 設置隔離級別爲讀已提交,可解決髒讀。
    -- read committed 讀已提交
    set session transaction isolation level read committed;
    
  2. 引發的問題:兩個併發的事務,“事務A:查詢”、“事務B:更新”,事務A事先讀取了數據,事務B緊接着更新了數據,並提交了事務,而事務A再次讀取該數據時,數據已經發生了改變,即我們所說的不可重複讀。

1.5.3 不可重複讀

  1. 設置隔離級別爲不可重複讀,可解決不可重複讀。
    -- repeatable read 不可重複讀
    set session transaction isolation level repeatable read;
    
  2. 引發的問題:兩個併發的事務,“事務A:更新”、“事務B:更新”,事務A事先更新了數據,事務B緊接着又把更新了回來,並提交了事務,而事務A再次讀取該數據時,發現數據沒有發生改變,即我們所說的幻讀。

1.5.4 可串行化

  1. 設置隔離級別爲可串行化,可解決一切問題。
    -- serializable 可串行化
    set session transaction isolation level serializable;
    
  2. 如果有一個連接的隔離級別設置爲了串行化,那麼誰先打開了事務,誰就有了先執行的權利,誰後打開事務,誰就只能等着,等前面的那個事務,提交或者回滾後,才能執行。這種隔離級別一般比較少用,容易造成性能上的問題,效率比較低。

1.6 鎖機制

  1. 解決丟失更新問題
    • 悲觀鎖(Pessimistic Locking)
    • 樂觀鎖(Optimistic Locking)

1.6.1 悲觀鎖

  1. 悲觀鎖原理:使用數據庫內部鎖機制,進行數據庫表的鎖定。就是在A管理員修改數據時,A管理員就將數據鎖定,此時B管理員無法進行修改、查詢。避免兩個事務同時修改,也就解決了丟失更新問題。
  2. 在查詢的時候,加入for update

1.6.2 樂觀鎖

  1. 樂觀鎖原理:使用的不是數據庫的鎖機制,而是一個特殊標記字段,通過控制字段狀態和內容得知數據是否發生了併發訪問。進行數據修改時,數據庫會檢測version字段或者時間戳是否與原來的一致。若不一致,拋出異常,提醒更新。
  2. 要求程序員自己控制。可以通過給數據表添加自增的version字段或時間戳timestamp

END

感謝看到這裏的各位讀者朋友們,如果你感到本文寫的不錯,就順手點個贊👍收藏一下,也可以關注一波~~

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