1. 程式人生 > 實用技巧 >MySQL 事務 異常 事務隔離的級別

MySQL 事務 異常 事務隔離的級別

MySQL 事務 異常 事務隔離的級別

 

事務

在你操作資料庫的同時,有可能其他使用者還會不斷地對資料進行增刪改查操作。為了避免並行進行時出現混亂,就產生了“事務”。事務就是要保證一組資料庫操作,要麼全部成功要麼全部失敗,以此來保證不混亂。

事務支援是在引擎層實現的,MySQL支援多系統,不是所有引擎都支援事務。

 

事務的特性(ACID)

  • Atomicity(原子性):原子即不可分割,即事務是進行資料處理的基本單位
  • Consistency(一致性):事務使資料庫從一種狀態到另一種狀態,且資料庫的約束性不被破壞
  • Isolation(隔離性):指事務之間保持獨立性,不被其他事務影響的特性。分為四個隔離等級
  • Durability(永續性):事務提交後對資料庫的修改時“持久的”,永續性是用過日誌來實現的(回滾日誌+重做日誌)。即使發生故障,資料修改依然有效,因為當事務完成,日誌就會被更新,我們依然可以恢復到最後一次成功提交事務時的狀態。

原子性是基礎,一致性是約束,隔離性是手段,永續性是目的。

 

事務操作方式

Mysql預設autocommit = 1compelation = 0

autocommit = 1:每條SQL都自動提交

autocommit = 0:不論是否使用START TRANSACTONBEGIN,總要使用commit才能提交。有些客戶端框架會預設連線後先執行set autocommit = 0

的命令。這就容易造成長連線,從而造成長事務。

儘量不要長事務,詳見"可重複讀的實現"

compelation = 0:執行commit僅提交事務

compelation = 1:commit時相當於commit and chain,開啟鏈式事務,提交事務後開啟一個相同的事務

compelation = 2: commit時相當於commit and release,提交後會自動斷開伺服器連線

在MySQL預設的設定下

  • 使用START TRANSACTIONBEGIN顯式開啟一個事務,然後再用COMMIT提交

  • 使用ROLLBACK進行回滾,或者回滾到ROLLBACK TO [SAVEPOINT]

    。例如,在一個事務中為有唯一約束(包含主鍵)的欄位插入了 多條有相同值的該欄位記錄。 那麼,在mysql預設情況下就會產生錯誤,再執行ROLLBACK後就會只有未插入前的資料。如下列程式碼,執行後前後查詢結果一致。

    SELECT * FROM t;
    ...
    BEGIN;
    INSERT INTO t id VALUES 1;
    INSERT INTO t id VALUES 1;
    ROLLBACK;
    SELECT * FROM t;
    

    需要注意的是,插入錯誤後只有手動ROLLBACK後才會執行後前後查詢結果一致,不執行ROLLBACK的結果是後面的查詢結果會比前面多一條id為1的記錄。

 

三種異常

  • 髒讀:事務並行進行時,事務A讀到了事務B中新增但未提交的內容
  • 不可重複讀:事務並行進行,事務A對錶進行查詢時事務B對錶某行進行修改,導致事務A發現兩次讀取時的結果不同。
  • 幻讀:事務並行進行,事務A進行查詢時事務B對錶新增資料,導致事務A兩次讀取時讀到更多的資料。

 

用於解決異常的四個事務隔離級別

隔離得越嚴實,效率就會越低,我們需要在生產中尋找一個平衡點,按業務決定。

對於隔離級別從低到高分別是

  • 讀未提交(READ UNCOMMIT) --- 允許讀到未提交的資料 --- 不使用鎖,無法避免三種異常

  • 讀已提交(READ COMMIT) --- 只能讀到已提交的資料 --- 其本身可避免髒讀(也是Oracle和SQL

    Server預設的隔離級別) --- 可以編寫帶鎖的SQL語句來避免“不可重複讀”和“幻讀”

  • 可重複讀(REPEATABLE READ) --- 事務在執行期間看到的資料必須前後一致 --- 避免 “髒讀”和“不可重複讀” (是MySQL預設的隔離級別)

  • 可序列化(SERIALIZABLE)--- 將所有事務序列化,是最高隔離等級,可以避免所有異常,但是犧牲了併發性

隔離級別效用說明例題:

  • 讀未提交:V1=2 V2=2 V3=2
  • 讀已提交:V1=1 V2=2 V3=2
  • 可重複讀:V1=1 V2=1 V3=2 (事務在執行期間看到的資料必須一致)
  • 可序列化:V1=2 V2=2 V3=2 (序列化時B在執行“將1改為2”時會被鎖住,直到事務A被提交

查詢mysql當前的隔離等級

mysql> show variables like 'transaction_isolation';
+-----------------------+-----------------+
| Variable_name         | Value           |
+-----------------------+-----------------+
| transaction_isolation | REPEATABLE-READ |
+-----------------------+-----------------+
1 row in set (0.02 sec)

 

“可重複讀”的實現

  • 隔離的實現主要有讀寫鎖多版本併發處理(MVCC)兩種方式。(因為讀寫鎖降低了事務併發效率,為了讓讀寫之間也不衝突,就發明了MVCC)

  • 事務隔離的實現拓展閱讀見此處,有一定了解後再整理自己的版本

  • 下面簡單介紹MVCC利用ReadView(快照)實現可重複讀

  • MySQL中每條記錄在更新的時候都會同時記錄一條回滾日誌

在將1依次改為2,3,4的過程中,回滾日誌有如下記錄
ReadView_A:將2改為1
ReadView_B:將3改為2
ReadView_C:將4改為3
而當前:
	值為4

這就讓同一條記錄在系統中可以有多個版本,這就是MVCC,此時如果有"將4改為5"的事務,它並不會影響回滾日誌中的A、B、C。

  • 儘量不要使用長事務:長事務意味著有很多舊的ReadView(影響回滾空間,5.5之前甚至有可能回滾空間大於真實資料),這會導致佔用大量的儲存空間,而且長事務還會佔用鎖資源,有可能拖垮整個庫。

  • 刪除回滾日誌:預設情況下,系統會自行判斷,當沒有事務需要用到這些回滾日誌時就會被刪除。在上述例子中,如果修改為4之後就commit(相當短的事務),即會造成“沒有事務需要用到這些回滾日誌”,上述回滾日誌就會被刪除。

    也就是說事務未提交前可以回滾,提交後回滾日誌會被刪除(5.7實驗通過)