您好,登錄后才能下訂單哦!
下文給大家帶來有關MySQL數據庫的事務隔離和MVCC內容,相信大家一定看過類似的文章。我們給大家帶來的有何不同呢?一起來看看正文部分吧,相信看完MySQL數據庫的事務隔離和MVCC你一定會有所收獲。
1. 什么是事務?
事務必須服從ISO/IEC所制定的ACID原則。ACID是原子性(atomicity)、一致性(consistency)、隔離性(isolation)、持久性(durability)的縮寫,這四種狀態的意思是:
1.原子性(Atomicity)
原子性是指事務包含的所有操作要么全部成功,要么全部失敗回滾,這和前面兩篇博客介紹事務的功能是一樣的概念,因此事務的操作如果成功就必須要完全應用到數據庫,如果操作失敗則不能對數據庫有任何影響。
2.一致性(Consistency)
一致性是指事務必須使數據庫從一個一致性狀態變換到另一個一致性狀態.
3.隔離性(Isolation)
在事務正確提交之前,不允許把事務對該數據的改變提供給任何其他事務,即在事務正確提交之前,它可能的結果不應該顯示給其他事務.
4.持久性(Durability)
持久性是指一個事務一旦被提交了,那么對數據庫中的數據的改變就是永久性的,即便是在數據庫系統遇到故障的情況下也不會丟失提交事務的操作。
2. 事務的作用
當多個線程都開啟事務操作數據庫中的數據時,數據庫系統要能進行隔離操作,以保證各個線程獲取數據的準確性.
3. 遇到的并發問題
1.第一類丟失更新:A事務撤銷時,把已經提交的B事務的更新數據覆蓋了.
2.第二類丟失更新:A事務覆蓋B事務已經提交的數據,造成B事務所做操作丟失.
3.臟讀:A事務讀取了事務B中未提交的數據.
4.不可重復讀:A事務多次讀取的值不同,因為該值被B事務修改并提交了.
5.幻讀:A事務兩次讀之間,B事務插入了數據.
4. 如何解決上面的問題呢?
為了解決上面的問題,開發者為MySQL數據庫設計了以下四種事務隔離級別:
1.Read Uncommitted(未提交讀):允許臟讀,也就是可能讀取到其他會話中未提交事務修改的數據.
2.Read Committed(提交讀):只能讀取到已經提交的數據。Oracle等多數數據庫默認都是該級別 (不重復讀).
3.Repeated Read(可重復讀):可重復讀。在同一個事務內的查詢都是事務開始時刻一致的,InnoDB默認級別。在SQL標準中,該隔離級別消除了不可重復讀,但是還存在幻象讀,但是innoDB解決了幻讀.
4.Serializable(串行讀):完全串行化的讀,每次讀都需要獲得表級共享鎖,讀寫相互都會阻塞.
隔離級別 | 臟讀 | 不可重復度 | 不幻讀 |
---|---|---|---|
Read Uncommitted(未提交讀) | 可能 | 可能 | 可能 |
Read Committed(提交讀) | 不可能 | 可能 | 可能 |
Repeated Read(可重復讀) | 不可能 | 不可能 | 可能 |
Serializable(串行讀) | 不可能 | 不可能 | 不可能 |
1.查看全局或會話的事務隔離級別
SELECT @@global.tx_isolation, @@tx_isolation;
2.修改全局或會話的事務隔離級別
SET [SESSION|GLOBAL] TRANSACTION ISOLATION LEVEL [READ UNCOMMITTED|READ COMMITTED|REPEATABLE READ|SERIALIZABLE]
以下將先介紹數據庫所涉及的鎖.
1.鎖簡介
數據庫中的鎖是指一種軟件機制,用來控制防止某個用戶(進程會話)在已經占用了某種數據資源時,其他用戶做出影響本用戶數據操作或導致數據非完整性和非一致性問題發生的手段。
2.鎖的級別
按照鎖級別劃分,鎖可分為共享鎖、排他鎖。
針對同一塊數據,多個讀操作可以同時進行而不會互相影響。共享鎖只針對UPDATE時候加鎖,在未對UPDATE操作提交之前,其他事務只能夠獲取最新的記錄但不能夠UPDATE操作。
當前寫操作沒有完成前,阻斷其他寫鎖和讀鎖。
3.鎖的粒度
按鎖的粒度劃分,鎖可分為表級鎖、行級鎖、頁級鎖。
開銷大,加鎖慢,會出現死鎖,鎖定力度最小,發生鎖沖突的概率最低,并發度高。
開銷小,加鎖快,不會出現死鎖,鎖定力度大,發生沖突所的概率高,并發度低。
開銷和加鎖時間介于表鎖和行鎖之間,會出現死鎖,鎖定力度介于表和行行級鎖之間,并發度一般。
1.基本思想:總是假設最壞的情況,每次去拿數據的時候都認為別人會修改,所以每次在拿數據的時候都會上鎖,這樣別人想拿這個數據就會阻塞直到它拿到鎖(共享資源每次只給一個線程使用,其它線程阻塞,用完后再把資源轉讓給其它線程)。傳統的關系型數據庫里邊就用到了很多這種鎖機制,比如行鎖,表鎖等,讀鎖,寫鎖等,都是在做操作之前先上鎖.所以不管沖突是否真的發生,都會使用鎖機制。
2.悲觀鎖功能:
1.基本思想:總是假設最好的情況,每次去拿數據的時候都認為別人不會修改,所以不會上鎖,但是在更新的時候會判斷一下在此期間別人有沒有去更新這個數據,可以使用版本號機制和CAS算法實現。樂觀鎖適用于多讀的應用類型,這樣可以提高吞吐量.
2.解釋:樂觀鎖是一種思想,樂觀鎖不會鎖住任何東西,也就是說,它不依賴數據庫的事務機制,樂觀鎖完全是應用系統層面的東西。所以它不是一種鎖機制.如果使用樂觀鎖,那么數據庫就必須加版本字段,否則就只能比較所有字段,但因為浮點類型不能比較,所以實際上沒有版本字段是不可行的
一般是在數據表中加上一個數據版本號version字段,表示數據被修改的次數,當數據被修改時,version值會加一。當線程A要更新數據值時,在讀取數據的同時也會讀取version值,在提交更新時,若剛才讀取到的version值為當前數據庫中的version值相等時才更新,否則重試更新操作,直到更新成功。
1.核心思想:Compare and Swap,即比較再交換。
2.過程:假設有A線程準備去修改內存中變量名為name的值,因此A線程會用以前自己讀到的name變量值和此刻name的值做對比,如果一樣,則表明在變量值沒被修改過,因此可以更新修改,否則更新失敗.
前面說過,MySQL默認實現了可重復讀的事務隔離級別,但是不能解決幻讀的問題,然而在MySQL數據庫使用可重復讀的事務隔離條件下,并未發生幻讀.MySQL使用MVCC(多版本并發控制)進行了控制.
1.MVCC:是multiversion concurrency control的簡稱,也就是多版本并發控制,是個很基本的概念。MVCC的作用是讓事務在并行發生時,在一定隔離級別前提下,可以保證在某個事務中能實現一致性讀,也就是該事務啟動時根據某個條件讀取到的數據,直到事務結束時,再次執行相同條件,還是讀到同一份數據,不會發生變化(不會看到被其他并行事務修改的數據)。
2.read view:InnoDB MVCC使用的內部快照的意思。在不同的隔離級別下,事務啟動時(有些情況下,可能是SQL語句開始時)看到的數據快照版本可能也不同。在上面介紹的幾個隔離級別下會用到 read view。
3.快照讀: 就是所謂的根據read view去獲取信息和數據,不會加任何的鎖。
4.當前讀:前讀會獲取得到所有已經提交數據,按照邏輯上來講的話,在一個事務中第一次當前讀和第二次當前讀的中間有新的事務進行DML操作,這個時候倆次當前讀的結果應該是不一致的,但是實際的情況卻是在當前讀的這個事務還沒提交之前,所有針對當前讀的數據修改和插入都會被阻塞,主要是因為next-key lock解決了當前讀可能會發生幻讀的情況。
next-key lock當使用主鍵索引進行當前讀的時候,會降級為record lock(行鎖)
InnoDB支持MVCC多版本控制,其中READ COMMITTED和REPEATABLE READ隔離級別是利用consistent read view(一致讀視圖)方式支持的。所謂的consistent read view就是在某一時刻給事務系統trx_sys打snapshot(快照),把當時的trx_sys狀態(包括活躍讀寫事務數組)記下來,之后的所有讀操作根據其事務ID(即trx_id)與snapshot中trx_sys的狀態做比較,以此判斷read view對事務的可見性。
REPEATABLE READ隔離級別(除了GAP鎖之外)和READ COMMITTED隔離級別的差別是創建snapshot時機不同。REPEATABLE READ隔離級別是在事務開始時刻,確切的說是第一個讀操作創建read view的時候,READ COMMITTED隔離級別是在語句開始時刻創建read view的。這就意味著REPEATABLE READ隔離級別下面一個事務的SELECT操作只會獲取一個read view,但是READ COMMITTED隔離級別下一個事務是可以獲取多個read view的。
創建/關閉read view需要持有trx_sys->mutex,會降低系統性能,5.7版本對此進行優化,在事務提交時session會cache只讀事務的read view。
在InnoDB中,創建一個新事務的時候,InnoDB會將當前系統中的活躍事務列表(trx_sys->trx_list)創建一個副本(read view),副本中保存的是系統當前不應該被本事務看到的其他事務id列表。當用戶在這個事務中要讀取該行記錄的時候,InnoDB會將該行當前的版本號與該read view進行比較。
具體的算法如下:
設該行的當前事務id為trx_id,read view中最早的事務id為trx_id_min, 最遲的事務id為trx_id_max。
如果trx_id< trx_id_min的話,那么表明該行記錄所在的事務已經在本次新事務創建之前就提交了,所以該行記錄的當前值是可見的。
如果trx_id>trx_id_max的話,那么表明該行記錄所在的事務在本次新事務創建之后才開啟,所以該行記錄的當前值不可見。
如果trx_id_min <= trx_id <= trx_id_max, 那么表明該行記錄所在事務在本次新事務創建的時候處于活動狀態,從trx_id_min到trx_id_max進行遍歷,如果trx_id等于他們之中的某個事務id的話,那么不可見,如圖:
從該行記錄的DB_ROLL_PTR指針所指向的回滾段中取出最新的undo-log的版本號的數據,將該可見行的值返回。
需要注意的是,新建事務(當前事務)與正在內存中commit 的事務不在活躍事務鏈表中。
在具體多版本控制中我們先來看下源碼:
函數:read_view_sees_trx_id。 read_view中保存了當前全局的事務的范圍: 【low_limit_id, up_limit_id】 1.當行記錄的事務ID小于當前系統的最小活動id,就是可見的。 if (trx_id < view->up_limit_id) { return(TRUE); } 2.當行記錄的事務ID大于當前系統的最大活動id(也就是尚未分配的下一個事務的id),就是不可見的。 if (trx_id >= view->low_limit_id) { return(FALSE); } 3.當行記錄的事務ID在活動范圍之中時,判斷是否在活動鏈表中,如果在就不可見,如果不在就是可見的。 for (i = 0; i < n_ids; i++) { trx_id_t view_trx_id = read_view_get_nth_trx_id(view, n_ids - i - 1); if (trx_id <= view_trx_id) { return(trx_id != view_trx_id); } }
Read view 圖解:
對于上文關于MySQL數據庫的事務隔離和MVCC,大家覺得是自己想要的嗎?如果想要了解更多相關,可以繼續關注我們的行業資訊板塊。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。