mysql事務隔離 原

事務

MySQL 事務主要用於處理操作量大,複雜度高的數據。

比如:在人員管理系統中,你刪除一個人員,你即需要刪除人員的基本資料,也要刪除和該人員相關的信息,如信箱,文章等等,這樣,這些數據庫操作語句就構成一個事務!

特性

一般來說,事務是必須滿足4個條件(ACID):原子性(Atomicity,或稱不可分割性)、一致性(Consistency)、隔離性(Isolation,又稱獨立性)、持久性(Durability)。

  1. 原子性:一個事務(transaction)中的所有操作,要麼全部完成,要麼全部不完成,不會結束在中間某個環節。事務在執行過程中發生錯誤,會被回滾(Rollback)到事務開始前的狀態,就像這個事務從來沒有執行過一樣。
  2. 一致性:在事務開始之前和事務結束以後,數據庫的完整性沒有被破壞。這表示寫入的資料必須完全符合所有的預設規則,這包含資料的精確度、串聯性以及後續數據庫可以自發性地完成預定的工作。
  3. 隔離性:數據庫允許多個併發事務同時對其數據進行讀寫和修改的能力,隔離性可以防止多個事務併發執行時由於交叉執行而導致數據的不一致。事務隔離分爲不同級別,包括讀未提交(Read uncommitted)、讀提交(read committed)、可重複讀(repeatable read)和串行化(Serializable)。
  4. 持久性:事務處理結束後,對數據的修改就是永久的,即便系統故障也不會丟失。

隔離級別

ANSI SQL標準定義的四個隔離級別爲:

  1. READ UNCOMMITTED(未提交讀),事務中的修改,即使沒有提交,在其他事務也都是可見的。事務可以讀取未提交的數據,這也被稱爲髒讀。
  2. READ COMMITTED(提交讀),一個事務從開始直到提交之前,所做的任何修改對其他事務都是不可見的。這個級別有時候也叫做不可重複讀,因爲兩次執行相同的查詢,可能會得到不一樣的結果。因爲在這2次讀之間可能有其他事務更改這個數據,每次讀到的數據都是已經提交的。
  3. REPEATABLE READ(可重複讀),解決了髒讀,也保證了在同一個事務中多次讀取同樣記錄的結果是一致的。但是理論上,可重讀讀隔離級別還是無法解決另外一個幻讀的問題,指的是當某個事務在讀取某個範圍內的記錄時,另外一個事務也在該範圍內插入了新的記錄,當之前的事務再次讀取該範圍內的記錄時,會產生幻行。
  4. SERIALIZABLE(可串行化),它通過強制事務串行執行,避免了前面說的幻讀的問題。

幾個可能產生的錯誤:

  1. 髒讀(dirty read):一個事務可以讀取另一個尚未提交事務的修改數據。
  2. 不可重複讀(nonrepeatable read):在同一個事務中,同一個查詢在T1時間讀取某一行,在T2時間重新讀取這一行時候,這一行的數據已經發生修改,可能被更新了(update),也可能被刪除了(delete)。
  3. 幻讀(phantom read):在同一事務中,同一查詢多次進行時候,由於其他插入操作(insert)的事務提交,導致每次返回不同的結果集。

InnoDB採用MVCC來支持高併發,並實現了四個標準的隔離級別。其默認級別是REPEATABLE READ(可重複讀),並且通過間隙鎖(next-key locking)策略防止幻讀的出現。間隙鎖使得InnoDB不僅僅鎖定查詢涉及的行,還會對索引中的間隙進行鎖定,以防止幻影的插入。 隔離級別越低,事務請求的鎖越少或保持鎖的時間就越短。所以很多數據庫系統默認的事務隔離級別是READ COMMITTED。質疑SERIALIZABLE隔離級別的性能,但是InnoDB存儲引擎認爲兩者的開銷是一樣的,所以默認隔離級別使用REPEATABLE READ。

用命令設置當前會話或全局會話的事務隔離級別。

SET [GLOBAL | SESSION] TRANSACTION ISOLATION LEVEL 
{
    READ UNCOMMITTED | READ COMMITTED | REPEATABLE READ | SERIALIZABLE
}
//配置文件
[mysqld]
transaction-isolation = READ-COMMITTED
//查看事務級別
select @@tx_ioslation;  //當前
select @@global.tx_isolation; //全局

MYSQL處理事務隔離(MVCC)

重點:主要是將表鎖降低到了行鎖。

Multi-Version Concurrency Control 多版本併發控制,MVCC 是一種併發控制的方法,一般在數據庫管理系統中,實現對數據庫的併發訪問;在編程語言中實現事務內存。

英文好難: Concurrency [kən'kʌrəns] Con curren cy

MVCC會保存某個時間點上的數據快照。這意味着事務可以看到一個一致的數據視圖,不管他們需要跑多久。這同時也意味着不同的事務在同一個時間點看到的同一個表的數據可能是不同的。

通過爲每一行記錄添加兩個額外的隱藏的值來實現MVCC,這兩個值一個記錄這行數據何時被創建,另外一個記錄這行數據何時過期(或者被刪除)。但是InnoDB並不存儲這些事件發生時的實際時間,相反它只存儲這些事件發生時的系統版本號。這是一個隨着事務的創建而不斷增長的數字。每個事務在事務開始時會記錄它自己的系統版本號。每個查詢必須去檢查每行數據的版本號與事務的版本號是否相同。讓我們來看看當隔離級別是REPEATABLE READ時這種策略是如何應用到特定的操作的:

SELECT InnoDB必須每行數據來保證它符合兩個條件:

  1. InnoDB必須找到一個行的版本,它至少要和事務的版本一樣老(也即它的版本號不大於事務的版本號)。這保證了不管是事務開始之前,或者事務創建時,或者修改了這行數據的時候,這行數據是存在的。
  2. 這行數據的刪除版本必須是未定義的或者比事務版本要大。這可以保證在事務開始之前這行數據沒有被刪除。這裏的不是真正的刪除數據,而是標誌出來的刪除。真正意義的刪除是在commit的時候。

符合這兩個條件的行可能會被當作查詢結果而返回。

  • INSERT:InnoDB爲這個新行記錄當前的系統版本號。
  • DELETE:InnoDB將當前的系統版本號設置爲這一行的刪除ID。
  • UPDATE:InnoDB會寫一個這行數據的新拷貝,這個拷貝的版本爲當前的系統版本號。它同時也會將這個版本號寫到舊行的刪除版本里。

這種額外的記錄所帶來的結果就是對於大多數查詢來說根本就不需要獲得一個鎖。他們只是簡單地以最快的速度來讀取數據,確保只選擇符合條件的行。這個方案的缺點在於存儲引擎必須爲每一行存儲更多的數據,做更多的檢查工作,處理更多的善後操作。

MVCC只工作在REPEATABLE READ和READ COMMITED隔離級別下。READ UNCOMMITED不是MVCC兼容的,因爲查詢不能找到適合他們事務版本的行版本;它們每次都只能讀到最新的版本。SERIABLABLE也不與MVCC兼容,因爲讀操作會鎖定他們返回的每一行數據 [1] 。

個人理解: 當鎖發生時,創建數據副本,副本的版本 = 事務的版本。

模擬一個數據結構

struct data {
lock Lock,
     value {
             line data_value,
             version int,
            next *data,
       },
     lockversion map[int]int
}

當事務開啓時,data1 = clone data; data.next = data1; data.version += 1; data.locked;

當事務結束時, data.next != null data = data.next; (要釋放之前的data); data.unlocked;

lockversionmap 對應的是一個map 事務ID對應數據版本ID。

當鎖爲其他級別時,

  • RUC級別 讀原始數據 不產生副本。
  • RC級別 讀到可能已經修改的數據,產生副本。
  • RR級別 如果多次讀的話,並行有其他事務,可能結果不一致。
  • 當鎖爲串行鎖的時候,一旦發現locked,就必須等待鎖結束,纔會讀取數據。

補充

  1. SQL規範所規定的標準,不同的數據庫具體的實現可能會有些差異
  2. mysql中默認事務隔離級別是可重複讀時並不會鎖住讀取到的行
  3. 事務隔離級別爲讀提交時,寫數據只會鎖住相應的行
  4. 事務隔離級別爲可重複讀時,如果有索引(包括主鍵索引)的時候,以索引列爲條件更新數據,會存在間隙鎖間隙鎖、行鎖、下一鍵鎖的問題,從而鎖住一些行;如果沒有索引,更新數據時會鎖住整張表。
  5. 事務隔離級別爲串行化時,讀寫數據都會鎖住整張表
  6. 隔離級別越高,越能保證數據的完整性和一致性,但是對併發性能的影響也越大,魚和熊掌不可兼得啊。對於多數應用程序,可以優先考慮把數據庫系統的隔離級別設爲Read Committed,它能夠避免髒讀取,而且具有較好的併發性能。儘管它會導致不可重複讀、幻讀這些併發問題,在可能出現這類問題的個別場合,可以由應用程序採用悲觀鎖或樂觀鎖來控制。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章