事務及隔離級別
1.事務
1.1事務的ACID屬性
-
原子性(Atomicity): 一個事務中的操作要求要麼全部執 行,要麼全部不執行.
-
一致性(Consistency): 在外部看來,數據庫中的數據總 是正確的.
-
隔離性(Isolation): 儘管多個事務在併發執行,但從外 部看來,具有多個事務串行執行的效果.
-
持久性(Durability):一個事務一旦提交了,即使隨後發 生故障,其結果在數據庫中不會丟失.
2.隔離級別
2.1四種隔離級別
- 讀未提交、讀提交、可重複讀、串行化
- 讀未提交(Read-uncommitted):一個事務還沒提交時,它做的變更就能被別的事務看到。
- 讀提交(Read-committed):一個事務提交之後,它做的變更纔會被其他事務看到。
- 可重複讀(Repeatable-read):一個事務執行過程中看到的數據,總是跟這個事務在啓動時看到的數據是一致的。當然未提交變更對其他事務也是不可見的。
- 串行化(Serializable):對於同一行記錄,“寫”會加寫鎖,讀會加“讀鎖”,當出現讀寫鎖衝突的時候,後訪問的事務必須等前一個事務執行完成,才能繼續執行。
2.2事務的併發問題
-
髒讀(dirty read):如果一個事務讀到了另一個未提交事務修改過的數據。
事務A 事務B 事務開始 餘額100元 事務開始 存入100元 查詢餘額爲200元 提交事務 回滾 -
不可重讀(non-repeatable read):如果一個事務只能讀到另一個已經提交的事務修改過的數據,並且其他事務每對該數據進行一行修改並提交後,該事務都能查詢得到最新值。
事務A 事務B 查詢餘額爲200元,事務先不提交 取出100元,並提交事務 再次查詢餘額爲100元 -
幻讀(phantom read):如果一個事務先根據某些條件查詢出一些記錄,之後另一個事務又向表中插入了符合這些條件的記錄,原先的事務再次按照該條件查詢時,能把另一個事務插入的記錄也讀出來。
事務A 事務B 查詢表中有幾條數據,結果爲100條 插入一條新數據 查詢表中有幾條數據,結果爲101條 -
不可重複讀的和幻讀很容易混淆,不可重複讀側重於修改,幻讀側重於新增或刪除。解決不可重複讀的問題只需鎖住滿足條件的行,解決幻讀需要鎖表。
2.3隔離級別會發生的事務併發問題
事務隔離級別 | 髒讀 | 不可重複讀 | 幻讀 |
---|---|---|---|
讀未提交 | ✔ | ✔ | ✔ |
讀提交 | ✖ | ✔ | ✔ |
可重複讀 | ✖ | ✖ | ✔ |
串行化 | ✖ | ✖ | ✖ |
2.4隔離級別實現的原理
- 排他鎖(X鎖,Exclusive Lock):T事務給A對象加X鎖後,只允許T事務讀取和修改A對象,其他事務不能再對A對象加任何類型的鎖。
- 共享鎖(S鎖,Share Lock):主要用於讀數據,一數據加了S鎖還可以加S鎖,但不能加X鎖。
- 更新鎖(Update Lock):更新鎖是爲了防止死鎖而設立的。當SQL Server 準備更新數據時,它首先對數據對象作更新鎖鎖定,這樣數據將不能被修改,但可以讀取。等到SQL Server 確定要進行更新數據操作時,它會自動將更新鎖換爲排他鎖。但當對象上有其它鎖存在時,無法對其作更新鎖鎖定。
- 隔離級別的原理:(通過加鎖或MVCC)
- 讀未提交的原理:讀取的時候不加鎖;更新的時候加排他鎖,事務結束就釋放。
- 讀提交的原理:讀取的時候加共享鎖,讀完就釋放;更新的時候加排他鎖,事務結束就釋放。
- 可重複讀的原理:讀取的時候加共享鎖,事務結束才釋放;更新的時候加排他鎖,事務結束就釋放。
- 串行化的原理:嚴格有序串行執行,事務不能併發執行。
3.MVCC(Multi-Version Concurrency Control多版本併發控制)
3.1出現原因
- “串行化”隔離級別,雖然不會出錯,但是效率實在太低了。 避免使用!!
- “可重複讀”,雖然會出現幻讀,但是也能忍受。但爲了實現可重複讀, 需要在事務中對讀操作加鎖,並且得持續到整個事務結束,效率也一般,可選擇使用。
3.2實現原理
- MVCC的實現,通過保存數據在某個時間點的快照來實現的。這意味着一個事務無論運行多長時間,在同一個事務裏能夠看到數據一致的視圖。根據事務開始的時間不同,同時也意味着在同一個時刻不同事務看到的相同表裏的數據可能是不同的。
- 隱藏字段:
- DB_TRX_ID(6字節):表示最近一次對本記錄行作修改(insert | update)的事務ID。至於delete操作,InnoDB認爲是一個update操作,不過會更新一個另外的刪除位,將行表示爲deleted。並非真正刪除。
- DB_ROLL_PTR(7字節):回滾指針,指向當前記錄行的undo log信息。
- DB_ROW_ID(6字節):隨着新行插入而單調遞增的行ID。理解:當表沒有主鍵或唯一非空索引時,innodb就會使用這個行ID自動產生聚簇索引。如果表有主鍵或唯一非空索引,聚簇索引就不會包含這個行ID了。
- Read View結構
- 其實Read View(讀視圖),跟快照、snapshot是一個概念。
- Read View主要是用來做可見性判斷的, 裏面保存了“對本事務不可見的其他活躍事務”。
- 其中包括幾個變量:
- low_limit_id:目前出現過的最大的事務ID+1,即下一個將被分配的事務ID。
- up_limit_id:活躍事務列表trx_ids中最小的事務ID,如果trx_ids爲空,則up_limit_id 爲 low_limit_id。因爲trx_ids中的活躍事務號是逆序的,所以最後一個爲最小活躍事務ID。
- trx_ids:Read View創建時其他未提交的活躍事務ID列表。意思就是創建Read View時,將當前未提交事務ID記錄下來,後續即使它們修改了記錄行的值,對於當前事務也是不可見的。
- creator_trx_id:當前創建事務的ID,是一個遞增的編號。
- Undo log
- undo log是爲回滾而用,具體內容就是copy事務前的數據庫內容(行)到undo buffer,在適合的時間把undo buffer中的內容刷新到磁盤。undo buffer與redo buffer一樣,也是環形緩衝,但當緩衝滿的時候,undo buffer中的內容會也會被刷新到磁盤;與redo log不同的是,磁盤上不存在單獨的undo log文件,所有的undo log均存放在主ibd數據文件中(表空間),即使客戶端設置了每表一個數據文件也是如此。
3.3一般認爲的MVCC
- 每行數據都存在一個版本,每次數據更新時都更新該版本
- 修改時Copy出當前版本隨意修改,各事務之間無干擾
- 保存時比較版本號,如果成功(commit),則覆蓋原記錄;失敗則放棄copy(rollback)
3.4INNODB的實現方式
- 事務以排他鎖的形式修改原始數據
- 把修改前的數據存放於undo log,通過回滾指針與主數據關聯
- 修改成功(commit)啥都不做,失敗則恢復undo log中的數據(rollback)
3.5區別
- 二者最本質的區別是,當修改數據時是否要排他鎖定,如果鎖定了還算不算是MVCC?
- Innodb的實現真算不上MVCC,因爲並沒有實現核心的多版本共存
- undo log中的內容只是串行化的結果,記錄了多個事務的過程,不屬於多版本共存。
- 但理想的MVCC是難以實現的,當事務僅修改一行記錄使用理想的MVCC模式是沒有問題的,可以通過比較版本號進行回滾;但當事務影響到多行數據時,理想的MVCC就無能爲力了。
- 比如,如果Transaciton1執行理想的MVCC,修改Row1成功,而修改Row2失敗,此時需要回滾Row1,但因爲Row1沒有被鎖定,其數據可能又被Transaction2所修改,如果此時回滾Row1的內容,則會破壞Transaction2的修改結果,導致Transaction2違反ACID。
- 理想MVCC難以實現的根本原因在於企圖通過樂觀鎖代替二段提交。修改兩行數據,但爲了保證其一致性,與修改兩個分佈式系統中的數據並無區別,而二提交是目前這種場景保證一致性的唯一手段。二段提交的本質是鎖定,樂觀鎖的本質是消除鎖定,二者矛盾,故理想的MVCC難以真正在實際中被應
用,Innodb只是借了MVCC這個名字,提供了讀的非阻塞而已。