MySQL:事務的隔離性
【參考文章】:數據庫的事務特性及隔離級別
1. 事務的四大特性
1.1 原子性(Atomicity)
原子性是指事務包含的所有操作要麽全部成功,要麽全部失敗回滾,這和前面兩篇博客介紹事務的功能是一樣的概念,因此事務的操作如果成功就必須要完全應用到數據庫,如果操作失敗則不能對數據庫有任何影響。
1.2 一致性(Consistency)
一致性是指事務必須使數據庫從一個一致性狀態變換到另一個一致性狀態,也就是說一個事務執行之前和執行之後都必須處於一致性狀態。
拿轉賬來說,假設用戶A和用戶B兩者的錢加起來一共是5000,那麽不管A和B之間如何轉賬,轉幾次賬,事務結束後兩個用戶的錢相加起來應該還得是5000,這就是事務的一致性。
1.3 隔離性(Isolation)
隔離性是當多個用戶並發訪問數據庫時,比如操作同一張表時,數據庫為每一個用戶開啟的事務,不能被其他事務的操作所幹擾,多個並發事務之間要相互隔離。
即要達到這麽一種效果:對於任意兩個並發的事務T1和T2,在事務T1看來,T2要麽在T1開始之前就已經結束,要麽在T1結束之後才開始,這樣每個事務都感覺不到有其他事務在並發地執行。
1.4 持久性(Durability)
持久性是指一個事務一旦被提交了,那麽對數據庫中的數據的改變就是永久性的,即便是在數據庫系統遇到故障的情況下也不會丟失提交事務的操作。
2. 事務的隔離級別
事務的隔離級別有4種,由低到高分別為Read uncommitted 、Read committed 、Repeatable read 、Serializable 。而且,在事務的並發操作中可能會出現臟讀,不可重復讀,幻讀。下面通過事例一一闡述它們的概念與聯系。
2.1 Read uncommitted
讀未提交,顧名思義,就是一個事務可以讀取另一個未提交事務的數據。
事例:老板要給程序員發工資,程序員的工資是3.6萬/月。但是發工資時老板不小心按錯了數字,按成3.9萬/月,該錢已經打到程序員的戶口,但是事務還沒有提交,就在這時,程序員去查看自己這個月的工資,發現比往常多了3千元,以為漲工資了非常高興。但是老板及時發現了不對,馬上回滾差點就提交了的事務,將數字改成3.6萬再提交。
分析:實際程序員這個月的工資還是3.6萬,但是程序員看到的是3.9萬。他看到的是老板還沒提交事務時的數據。這就是臟讀。
那怎麽解決臟讀呢?Read committed!讀提交,能解決臟讀問題。
2.2 Read committed
讀提交,顧名思義,就是一個事務要等另一個事務提交後才能讀取數據。
事例:程序員拿著信用卡去享受生活(卡裏當然是只有3.6萬),當他埋單時(程序員事務開啟),收費系統事先檢測到他的卡裏有3.6萬,就在這個時候!!程序員的妻子要把錢全部轉出充當家用,並提交。當收費系統準備扣款時,再檢測卡裏的金額,發現已經沒錢了(第二次檢測金額當然要等待妻子轉出金額事務提交完)。程序員就會很郁悶,明明卡裏是有錢的…
分析:這就是讀提交,若有事務對數據進行更新(UPDATE)操作時,讀操作事務要等待這個更新操作事務提交後才能讀取數據,可以解決臟讀問題。但在這個事例中,出現了一個事務範圍內兩個相同的查詢卻返回了不同數據,這就是不可重復讀。
那怎麽解決可能的不可重復讀問題?Repeatable read !
2.3 Repeatable read(mysql 默認隔離級別)
重復讀,就是在開始讀取數據(事務開啟)時,不再允許修改操作,可以進行INSERT操作(幻讀)
事例:程序員拿著信用卡去享受生活(卡裏當然是只有3.6萬),當他埋單時(事務開啟,不允許其他事務的UPDATE修改操作),收費系統事先檢測到他的卡裏有3.6萬。這個時候他的妻子不能轉出金額了。接下來收費系統就可以扣款了。
分析:重復讀可以解決不可重復讀問題。寫到這裏,應該明白的一點就是,不可重復讀對應的是修改,即UPDATE操作。但是可能還會有幻讀問題。因為幻讀問題對應的是插入INSERT操作,而不是UPDATE操作。
什麽時候會出現幻讀?
事例:程序員某一天去消費,花了2千元,然後他的妻子去查看他今天的消費記錄(全表掃描FTS,妻子事務開啟),看到確實是花了2千元,就在這個時候,程序員花了1萬買了一部電腦,即新增INSERT了一條消費記錄,並提交。當妻子打印程序員的消費記錄清單時(妻子事務提交),發現花了1.2萬元,似乎出現了幻覺,這就是幻讀。
那怎麽解決幻讀問題?Serializable!
2.4 Serializable 序列化
Serializable 是最高的事務隔離級別,在該級別下,事務串行化順序執行,可以避免臟讀、不可重復讀與幻讀。但是這種事務隔離級別效率低下,比較耗數據庫性能,一般不使用。
3. 事務隔離性
3.1 視圖(read-view)
在實現中,數據庫創建一個視圖,訪問時以視圖的邏輯結果為準。
讀未提交:沒有視圖的概念,直接返回記錄上的最新值;
讀已提交:每個SQL語句執行時創建視圖;
可重復讀:事務啟動時創建視圖,整個事務期間都是使用這個視圖;
串行化:直接加鎖避免並行訪問;
3.2 實現原理
在MySQL中,每條記錄在更新的時候同時會記錄一條回滾操作,痛過回滾操作可以得到上一個狀態的值;
假設一條記錄從1按順序被改為2,3,4,則在不同時間啟動的事務中,這條記錄的值可能為1,2,3,4;
同一條記錄可以存在多個版本忙著就是數據庫的多版本並發控制(MVCC),當系統中不存在比回滾日誌更早的 read-view 時,就可以刪除回滾日誌了;
長事務意味著系統裏面會存在很老的視圖,這個事務提交之前,他可能用到的回滾日誌都必須保留,這就導致大量空間的浪費;
4. 事務的啟動方式
1、用 BEGIN, ROLLBACK, COMMIT來實現
- BEGIN 開始一個事務
- ROLLBACK 事務回滾
- COMMIT 事務確認
2、直接用 SET 來改變 MySQL 的自動提交模式:
- SET AUTOCOMMIT=0 禁止自動提交
- SET AUTOCOMMIT=1 開啟自動提交
MySQL:事務的隔離性