文章目錄
關於MySQL的基礎可以看我的上一篇博客:初學者不點進去就太虧了,MYSQL數據庫超全知識點總結!
1 事務
Transaction其實指的一組操作,裏面包含許多個單一的邏輯。只要有一個邏輯沒有執行成功,那麼都算失敗。所有的數據都回歸到最初的狀態(回滾)
- 爲什麼要有事務?
爲了確保邏輯的成功。例子:銀行的轉賬。
1.1 命令行演示
-
開啓事務
start transaction;
-
提交或者回滾事務
commit; -- 提交事務,數據將會寫到磁盤上的數據庫 rollback; -- 數據回滾,回到最初的狀態。
-
關閉自動提交功能
-
演示事務
1.2 代碼演示
代碼裏面的事務,只是針對連接連接對象,如果再開一個連接對象,那麼那是默認的提交。
- 通過
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); } }
1.3 事務的特性
-
原子性: 事務中包含的邏輯,不可分割。
-
一致性: 事務執行前後。數據完整性
-
隔離性: 事務在執行期間不應該受到其他事務的影響
-
持久性: 事務執行成功,那麼數據應該持久保存到磁盤上
1.4 事務的安全隱患
不考慮隔離級別設置,那麼會出現以下問題。
1.4.1 讀
髒讀、不可重讀讀、幻讀.
- 髒讀: 一個事務讀到另外一個事務還未提交的數據
- 不可重複讀: 一個事務讀到了另外一個事務提交的數據 ,造成了前後兩次查詢結果不一致。
- 幻讀: 一個事務讀到了另一個事務insert的數據,造成前後查詢結果不一致
1.4.2 寫
丟失更新
- 丟失更新: 如果多個線程操作,基於同一個查詢結構對錶中的記錄進行修改,那麼後修改的記錄將會覆蓋前面修改的記錄,前面的修改就丟失掉了。
1.5 隔離級別
-
讀未提交(Read uncommitted)
引發問題:髒讀
-
讀已提交(Read committed)
解決:髒讀,引發:不可重複讀
-
可重複讀(Repeatable read)
解決:髒讀、不可重複讀,未解決:幻讀
-
可串行化(Serializable)
解決:髒讀、不可重複讀、幻讀
-
按效率劃分,從高到低
讀未提交 > 讀已提交 > 可重複讀 > 可串行化
-
按攔截程度 ,從高到底
可串行化 > 可重複讀 > 讀已提交 > 讀未提交
-
MySQL 默認的隔離級別是: 可重複讀
-
Oracle 默認的隔離級別是: 讀已提交
√:可能出現 ×:不會出現 | 髒讀 | 不可重複讀 | 幻讀 |
---|---|---|---|
Read uncommitted | √ | √ | √ |
Read committed | × | √ | √ |
Repeatable read | × | × | √ |
Serializable | × | × | × |
1.5.1 讀未提交
- 查看事務的隔離級別
-- 我安裝的是MySQL8,原先的select @@tx_isolation;不能用了 select @@transaction_isolation;
- 設置隔離級別爲讀未提交
-- read uncommitted 讀未提交 set session transaction isolation level read uncommitted;
- 引發的問題:兩個併發的事務,“事務A:更新”、“事務B:查詢”,事務B讀取了事務A尚未提交的數據,即我們所說的髒讀。
1.5.2 讀已提交
- 設置隔離級別爲讀已提交,可解決髒讀。
-- read committed 讀已提交 set session transaction isolation level read committed;
- 引發的問題:兩個併發的事務,“事務A:查詢”、“事務B:更新”,事務A事先讀取了數據,事務B緊接着更新了數據,並提交了事務,而事務A再次讀取該數據時,數據已經發生了改變,即我們所說的不可重複讀。
1.5.3 不可重複讀
- 設置隔離級別爲不可重複讀,可解決不可重複讀。
-- repeatable read 不可重複讀 set session transaction isolation level repeatable read;
- 引發的問題:兩個併發的事務,“事務A:更新”、“事務B:更新”,事務A事先更新了數據,事務B緊接着又把更新了回來,並提交了事務,而事務A再次讀取該數據時,發現數據沒有發生改變,即我們所說的幻讀。
1.5.4 可串行化
- 設置隔離級別爲可串行化,可解決一切問題。
-- serializable 可串行化 set session transaction isolation level serializable;
- 如果有一個連接的隔離級別設置爲了串行化,那麼誰先打開了事務,誰就有了先執行的權利,誰後打開事務,誰就只能等着,等前面的那個事務,提交或者回滾後,才能執行。這種隔離級別一般比較少用,容易造成性能上的問題,效率比較低。
1.6 鎖機制
- 解決丟失更新問題
- 悲觀鎖(Pessimistic Locking)
- 樂觀鎖(Optimistic Locking)
1.6.1 悲觀鎖
- 悲觀鎖原理:使用數據庫內部鎖機制,進行數據庫表的鎖定。就是在A管理員修改數據時,A管理員就將數據鎖定,此時B管理員無法進行修改、查詢。避免兩個事務同時修改,也就解決了丟失更新問題。
- 在查詢的時候,加入
for update
。
1.6.2 樂觀鎖
- 樂觀鎖原理:使用的不是數據庫的鎖機制,而是一個特殊標記字段,通過控制字段狀態和內容得知數據是否發生了併發訪問。進行數據修改時,數據庫會檢測version字段或者時間戳是否與原來的一致。若不一致,拋出異常,提醒更新。
- 要求程序員自己控制。可以通過給數據表添加自增的
version
字段或時間戳timestamp
。
END
感謝看到這裏的各位讀者朋友們,如果你感到本文寫的不錯,就順手點個贊👍收藏一下,也可以關注一波~~