【重溫mysql】4、事務
什麼是事務?
事務指的是當 DML 資料修改語句提交給資料庫後,要麼資料全部成功寫入、如若其中某項操作失敗則所有資料全部回滾到修改前狀態的機制。資料庫通過事務保證資料的完整性、一致性。
ACID
一個完整的事務,必然包含了如下4個特性:原子性、一致性、隔離性、永續性。
類別 | 描述 |
---|---|
原子性(Atomicity) | 事務作為一個整體被執行,包含在其中的對資料庫的操作要麼全部被執行,要麼都不執行。 |
一致性(Consistency) | 事務應確保資料庫的狀態從一個一致狀態轉變為另一個一致狀態。一致狀態的含義是資料庫中的資料應滿足完整性約束。 |
隔離性(Isolation) | 多個事務併發執行時,一個事務的執行不應影響其他事務的執行。 |
永續性(Durability) | 已被提交的事務對資料庫的修改應該永久儲存在資料庫中 |
多執行緒併發事務問題
在多執行緒環境中,對於同一條資料而言可能有多個執行緒同時在進行修改操作,即帶來了操作衝突。按照併發控制理論,要想解決衝突的問題,可以有如下兩種思路:
- 1、避免衝突發生,如序列化、加鎖等。
- 2、允許衝突發生,即允許資料有多個版本,如使用MVCC。
同時,多執行緒事務還存在隔離性的問題,當多個事務併發執行的時候,一個事務中能否感知到另外一個事務中的資料修改。資料庫理論中專門定義了一系列的術語來描述:
術語 | 簡介 |
---|---|
髒讀 | 一個事務可以讀到另外一個事務中未提交的資料。這往往會造成資料不一致 |
不可重複讀 | 同一事務中兩次讀取同一行,資料不一致的情況稱為不可重複讀 |
幻讀 | 同一事務中通過統計或其他彙總語句統計出來的資料不一致的情況 |
為解決上述問題,資料庫提出了事務隔離級別來與之對應。從下表可以看出,一致性越好,併發能力越差。
隔離級別 | 髒讀 | 不可重複讀 | 幻讀 |
---|---|---|---|
讀未提交(Read Uncommit) | 有 | 有 | 有 |
讀已提交(Read Commit) | 無 | 有 | 有 |
可重複讀(Repeatable) | 無 | 無 | 有 |
序列化(Serializable) | 無 | 無 | 無 |
InnoDB 事務
在計算機領域,資料一致性、與資料崩潰恢復通常是通過 WAL(Write Ahead Log) 技術來實現的——使用者如果對資料庫中的資料就行了修改,必須保證日誌先於資料落盤。在InnoDB 引擎中,通過binlog 與redolog 的兩段提交的方式(在事務執行前先寫redolog,執行結束前寫binlog)保證了資料的一致性和完整性,如若資料刷盤的過程中發生了異常,那麼當MySQL重啟的時候,根據日誌對資料進行恢復,就可以還原資料,保證資料的一致性。在MySQL中,日誌檔案是最重要的資料,而資料檔案反而沒有那麼重要。
MVCC 機制
InnoDB 引擎是一個多版本儲存引擎,它通過保留資料行的舊版本來實現事務。
快照讀與當前讀
MVCC 中,資料有兩種讀取形式:
- 快照讀:讀取的只是當前事務的可見版本,不用加鎖。Select 使用的是快照讀,因此查詢效能可以獲得極大的提高。
- 當前讀:讀取的是當前版本,需要加鎖,Insert、Update、Delete 使用的是當前讀。
Undo Log
舊版本資料行稱作undo日誌,這些資訊儲存在表空間的回滾段裡。通常我們的undo日誌被用來做兩件事:
- 回滾
- 一致性讀
undo log 是邏輯上的日誌,記錄當前事務操作前的該行資料的原始狀態。 可分為 insert undo log和update undo log。
型別 | 說明 |
---|---|
Insert Undo Log | 插入undo log在資料插入的時候產生,僅僅在回滾的時候才會被用到,因為該行是新增的,之前並不存在該資料,因此不存在多版本,可以在事務提交後就可以丟棄。 |
Update Undo Log | 更新undo log 不僅在回滾時候需要用到,由於其他事務中可能用到了該資料,因此也需要用於一致性讀。有可能其他事務也正在處理這條資料,因此只有當該條資料沒有事務的時候,才能丟棄這些日誌。因此我們需要關注事務的處理時長,如果一個事務長時間得不到釋放那麼對應的undolog日誌就不能夠被清除,當吞吐量較大的時候需要佔用較大的表空間 |
資料行格式
InnoDB 引擎會隱性的為每一行資料都增加如下三個隱藏欄位。InnoDB 通過這三個欄位的相互配合實現了事務。資料行的格式如下:
- DB_ROW_ID 即行主鍵,6個位元組,唯一標識一行資料,如若使用者沒有定義主鍵,InnoDB內部會自動建立一個行ID當作主鍵。
- DB_TRX_ID 最後操作事務ID,6個位元組,記錄最後一次操作改行資料的事務ID
- DB_ROW_PTR 回滾指標,指向回滾段中一行 undo 日誌。
具體MVCC實現原理參考《你真的懂MVCC嗎?來手動實踐一下?》,過程寫的非常詳細。
Delete過程
在InnoDB 中,刪除操作實際上是被當作一個更新操作來處理。當我們需要刪除一條資料的時候,首先InnoDB在記憶體中將該資料的刪除標識位以及其輔助索引刪除標識位都標識為已刪除,之後當該資料的undo log 可以被清除的時候,InnoDB 通過purge執行緒將該資料物理清除。
同樣InnoDB 的物理資料刪除也是一個邏輯刪除過程,它僅僅是將該資料行標記為已刪除,僅此而已。資料標記為刪除並不意味著該資料行所擁有的空間能夠立即被複用,後續可以被用到的時候, 該資料列才會被覆蓋掉。因此當我們通過delete 方法刪除所有資料的時候,我們可以看到.ibd檔案的大小並沒有減少。這種索引列可以複用卻沒有被實際使用的現象我們通常稱之為索引空洞,解決該問題我們可以通過重建表的方式來對磁碟進行回收。重建命令如下:
alert table table_name engine = InnoDB
複製程式碼
聚集索引與輔助索引區別
我們知道InnoDB 主鍵使用了聚集索引。對於聚集索引,葉子節點包含了該資料的所有欄位,因此當資料發生變化的時候可以直接對這一資料行進行替換。
對於輔助索引而言,情況可能有點不太一樣。 當我們更新了一個輔助索引,InnoDB 首先將之前的索引標記為刪除,然後插入新的索引記錄,被刪除的索引通過 purge 執行緒在後續被清除。因此如果輔助索引被其他事務更新或刪除,覆蓋索引將會失效,查詢需要回表。