1. 程式人生 > >事務的隔離級別與所帶來的問題

事務的隔離級別與所帶來的問題

## **一、事務的基本要素(ACID)**   **1、原子性(Atomicity):事務開始後所有操作,要麼全部做完,要麼全部不做,不可能停滯在中間環節。事務執行過程中出錯,會回滾到事務開始前的狀態,所有的操作就像沒有發生一樣。也就是說事務是一個不可分割的整體,就像化學中學過的原子,是物質構成的基本單位。**    **2、一致性(Consistency):事務開始前和結束後,資料庫的完整性約束沒有被破壞 。比如A向B轉賬,不可能A扣了錢,B卻沒收到。**    **3、隔離性(Isolation):同一時間,只允許一個事務請求同一資料,不同的事務之間彼此沒有任何干擾。比如A正在從一張銀行卡中取錢,在A取錢的過程結束前,B不能向這張卡轉賬。**    **4、永續性(Durability):事務完成後,事務對資料庫的所有更新將被儲存到資料庫,不能回滾。** ## 二、SQL標準的事務隔離級別(mysql預設為可重複讀,oracle預設為讀已提交) - **讀未提交(read uncommitted)是指,一個事務還沒提交時,它做的變更就能被別的事務看到。** - **讀已提交(read committed)是指,一個事務提交之後,它做的變更才會被其他事務看到。** - **可重複讀(repeatable read)是指,一個事務執行過程中看到的資料,總是跟這個事務在啟動時看到的資料是一致的。當然在可重複讀隔離級別下,未提交變更對其他事務也是不可見的。** - **序列化(serializable ),顧名思義是對於同一行記錄,“寫”會加“寫鎖”,“讀”會加“讀鎖”。當出現讀寫鎖衝突的時候,後訪問的事務必須等前一個事務執行完成,才能繼續執**行。 **例子** | 事務一 | 事務二 | | ------------------ | ------------------- | | 啟動事務A,查詢A=10 | 啟動事務B | | | 查詢A=10, 更新A=20 | | 查詢值a1 | | | | 提交事務B | | 查詢值a2 | | | 提交事務A | | | 查詢值a3 | | - 若隔離級別是“**讀未提交**”, 則a1的值就是20,雖然B沒提交2但是結果已經A看到了 - 若隔離級別是“**讀已提交**”,則a1是10,a2的值是2。事務B的更新在提交後才能被A看到。所以, a3的值也是2。 - 若隔離級別是“**可重複讀**”,則a1、a2是10,a3是20。之所以a2還是10,遵循的就是這個要求:事務在執行期間看到的資料前後必須是一致的。 - 若隔離級別是“序列化”,則在事務B執行“將A=10改成20”的時候,會被鎖住。直到事務A提交後,事務B才可以繼續執行。所以從A的角度看, a1、a2值是10,a3的值是20 ## 三、如何設定和檢視隔離級別 - 讀未提交:read uncommitted - 讀已提交:read committed - 可重複讀:repeatable read - 序列化:serializable **檢視隔離級別** ``` show variables like 'transaction_isolation'; ``` 設定innodb的事務級別方法是:set 作用域 transaction isolation level 事務隔離級別,例如~ SET [SESSION | GLOBAL] TRANSACTION ISOLATION LEVEL {READ UNCOMMITTED | READ COMMITTED | REPEATABLE READ | SERIALIZABLE} ``` set global transaction isolation level read committed; //全域性的 set session transaction isolation level read committed; //當前會話 ``` ## 四、事務隔離級別所帶來的問題 ![img](https://images2015.cnblogs.com/blog/156233/201604/156233-20160425233944502-1414059978.jpg) 這四種隔離級別採取不同的鎖型別來實現,若讀取的是同一個資料的話,就容易發生問題。例如: ​ 髒讀(Drity Read):某個事務已更新一份資料,另一個事務在此時讀取了同一份資料,由於某些原因,前一個RollBack了操作,則後一個事務所讀取的資料就會是不正確的。 ​ 不可重複讀(Non-repeatable read):在一個事務的兩次查詢之中資料不一致,這可能是兩次查詢過程中間插入了一個事務更新的原有的資料。 ​ 幻讀(Phantom Read):在一個事務的兩次查詢中資料筆數不一致,例如有一個事務查詢了幾列(Row)資料,而另一個事務卻在此時插入了新的幾列資料,先前的事務在接下來的查詢中,就會發現有幾列資料是它先前所沒有的。 解決方案 //todo ## 補充 - 事務隔離的實現:每條記錄在更新的時候都會同時記錄一條回滾操作。同一條記錄在系統中可以存在多個版本,這就是資料庫的多版本併發控制(MVCC)。 - 回滾日誌什麼時候刪除?系統會判斷當沒有事務需要用到這些回滾日誌的時候,回滾日誌會被刪除。 - 什麼時候不需要了?當系統裡麼有比這個回滾日誌更早的read-view的時候。 - 為什麼儘量不要使用長事務。長事務意味著系統裡面會存在很老的事務檢視,在這個事務提交之前,回滾記錄都要保留,這會導致大量佔用儲存空間。除此之外,長事務還佔用鎖資源,可能會拖垮庫。 - 事務啟動方式: - 一、顯式啟動事務語句,begin或者start transaction,提交commit,回滾rollback; - 二、set autocommit=0,該命令會把這個執行緒的自動提交關掉。這樣只要執行一個select語句,事務就啟動,並不會自動提交,直到主動執行commit或rollback或斷開連線。 - 建議使用方法一,如果考慮多一次互動問題,可以使用commit work and chain語法。在autocommit=1的情況下用begin顯式啟動事務,如果執行commit則提交事務。如果執行commit work and chain則提交事務並自動啟動下一個事務。 ​ **1**、事務隔離級別為讀提交時,寫資料只會鎖住相應的行   **2**、事務隔離級別為可重複讀時,如果檢索條件有索引(包括主鍵索引)的時候,預設加鎖方式是next-key 鎖;如果**檢索條件**沒有索引,更新資料時 會鎖住整張表。一個間隙被事務加了鎖,其他事務是不能在這個間隙插入記錄的,這樣可以防止幻讀。   **3、事務隔離級別為序列化時,讀寫資料都會鎖住整張表**    **4**、隔離級別越高,越能保證資料的完整性和一致性,但是對併發效能的影響也越大。\****    **5**、MYSQL MVCC實現機制參考連結:https://blog.csdn.net/whoamiyang/article/details/51901888\****    **6**、關於\*\*\*\*next-key 鎖可以參考連結:https://blog.csdn.net/bigtree_3721/article/details/73731377\*\*\*\*\****