事務隔離級別在數據庫的具體實現原理

本文參考鏈接  數據庫的事務隔離級別總結   數據庫事務、事務隔離級別以及鎖機制詳解

事務併發可能引起的問題 髒讀 幻讀 不可重複讀 

舉例:https://blog.csdn.net/lululove19870526/article/details/78480135

https://blog.csdn.net/seeker520/article/details/80398900

上文講到事務的四大特性 acid 那麼數據庫如何實現事務的特性呢
本文內容

目錄

InnoDB引擎的事務實現

數據庫隔離級別實現

互聯網項目中MySQL用什麼事務隔離級別

MySQL間隙鎖

RC與RR在鎖方面的區別

如何避免髒讀、不可重複讀、幻讀。

mysql innodb引擎的鎖機制(悲觀鎖)

mvcc(樂觀鎖)

快照讀 和當前讀 


InnoDB引擎的事務實現

InnoDB是mysql的一個存儲引擎,大部分人對mysql都比較熟悉,這裏簡單介紹一下數據庫事務實現的一些基本原理,在本地事務中,服務和資源在事務的包裹下可以看做是一體的。
在這裏插入圖片描述
       而事務的ACID是通過InnoDB日誌和鎖來保證。事務的隔離性是通過數據庫鎖的機制實現的,持久性通過redo log(重做日誌)來實現,原子性和一致性通過Undo log(回撤日誌)來實現
        Undo Log的原理很簡單,爲了滿足事務的原子性,在操作任何數據之前,首先將數據備份到一個地方(這個存儲數據備份的地方稱爲Undo Log)。然後進行數據的修改。如果出現了錯誤或者用戶執行了roll back語句,系統可以利用Undo Log中的備份將數據恢復到事務開始之前的狀態。
        和Undo Log相反,Redo Log記錄的是新數據的備份。在事務提交前,只要將RedoLog持久化即可,不需要將數據持久化。當系統崩潰時,雖然數據沒有持久化,但是RedoLog已經持久化。系統可以根據Redo Log的內容,將所有數據恢復到最新的狀態。

數據庫隔離級別實現

標準SQL規範中,定義了四個事務隔離級別:RU RC RR S

       各類流行的數據庫都實現了一些SQL標準中的事務隔離級別,但是他們的實現也是極其不一樣的。

       Oracle僅僅實現了RC 和 SERIALIZABLE隔離級別默認採用RC隔離級別,解決了髒讀。但是允許不可重複讀和幻讀。其SERIALIZABLE則解決了髒讀、不可重複讀、幻讀。

       MySQL默認採用RR隔離級別,SQL標準是要求RR解決不可重複讀的問題,但是因爲MySQL採用了間隙鎖(gap lock),所以實際上MySQL的RR隔離級別也解決了幻讀的問題。那麼MySQL的SERIALIZABLE是怎麼回事呢?MySQL的SERIALIZABLE採用了經典的實現方式,對讀和寫都加鎖。

互聯網項目中MySQL用什麼事務隔離級別

Mysql默認的事務隔離級別是可重複讀(Repeatable Read),那互聯網項目中Mysql也是用默認隔離級別,不做修改麼? OK,不是的,我們在項目中一般用讀已提交(Read Commited)這個隔離級別! 居然是讀已提交。

我們先來思考一個問題,在Oracle、SqlServer中都是選擇讀已提交(Read Commited)作爲默認的隔離級別,爲什麼Mysql不選擇讀已提交(Read Commited)作爲默認隔離級別,而選擇可重複讀(Repeatable Read)作爲默認的隔離級別呢?

我們先明白一點!項目中是不用讀未提交(Read UnCommitted)和串行化(Serializable)兩個隔離級別,原因有二: 採用讀未提交(Read UnCommitted),一個事務讀到另一個事務未提交讀數據,這個不用多說吧,從邏輯上都說不過去!採用串行化(Serializable),每個次讀操作都會加鎖,快照讀失效,一般是使用Mysql自帶的分佈式事務功能時才使用該隔離級別!也就是說,我們該糾結都只有一個問題,究竟隔離級別是用讀已經提交呢還是可重複讀? 接下來對這兩種級別進行對比,講講我們爲什麼選讀已提交(Read Commited)作爲事務隔離級別!

MySQL間隙鎖

當我們用範圍條件而不是相等條件檢索數據,並請求共享或排他鎖時,InnoDB會給符合條件的已有數據記錄的索引項加鎖;對於鍵值在條件範圍內但並不存在的記錄,叫做“間隙(GAP)”,InnoDB也會對這個“間隙”加鎖,這種鎖機制就是所謂的間隙鎖(GAP LOCK)。舉例來說,假如user表中只有101條記錄,其empid的值分別是 1,2,…,100,101,下面的SQL:

select * from  user where user_id > 100 for update;

這是一個範圍條件的檢索且要求加上排他鎖,InnoDB不僅會對符合條件的user_id值爲101的記錄加鎖,也會對user_id大於101(這些記錄並不存在)的“間隙”加鎖。

InnoDB使用間隙鎖的目的,一方面是爲了防止幻讀(爲了防止幻讀去鎖表則影響太大,會影響效率),以滿足相關隔離級別的要求,對於上面的例子,要是不使用間隙鎖,如果其他事務插入了user_id大於100的任何記錄,那麼本事務如果再次執行上述語句,就會發生幻讀;另外一方面,是爲了滿足其恢復和複製的需要(發生幻讀時的binlog,如果直接拿到備庫去執行會發生了主備數據不一致的嚴重問題)。

很顯然,在使用範圍條件檢索並鎖定記錄時,InnoDB這種加鎖機制會阻塞符合條件範圍內鍵值的併發插入,這往往會造成嚴重的鎖等待。因此,在實際應用開發中,尤其是併發插入比較多的應用,我們要儘量優化業務邏輯,儘量使用相等條件來訪問更新數據,避免使用範圍條件;當然,對一條不存在的記錄加鎖,也會有間隙鎖的問題。

間隙鎖在InnoDB的唯一作用就是防止其它事務的插入操作,以此來達到防止幻讀的發生,所以間隙鎖不分什麼共享鎖與排它鎖。如果InnoDB掃描的是一個主鍵、或是一個唯一索引的話,那InnoDB只會採用行鎖方式來加鎖,而不會使用Next-Key Lock的方式,也就是說不會對索引之間的間隙加鎖。

要禁止間隙鎖的話,可以把隔離級別降爲讀已提交,或者開啓參數innodb_locks_unsafe_for_binlog。

RC與RR在鎖方面的區別

1、RR要用到間隙鎖,而RC則沒有間隙鎖。因爲MySQL的RR需要間隙鎖來解決幻讀問題。而RC隔離級別則是允許存在不可重複讀和幻讀的。所以RC的併發一般要好於RR;在RR隔離級別下,存在間隙鎖,導致出現死鎖的機率比RC大的多;
2、 RC 隔離級別,通過 where 條件過濾之後,不符合條件的記錄上的行鎖,會被釋放掉,但是RR隔離級別,即使不符合where條件的記錄,也不會釋放行鎖和間隙鎖,所以從鎖方面來看,RC的併發應該要好於RR;
3、RC隔離級別時,事務中的每一條select語句會讀取到他自己執行時已經提交了的記錄,也就是每一條select都有自己的一致性讀ReadView; 而RR隔離級別時,事務中的一致性讀的ReadView是以第一條select語句的運行時,作爲本事務的一致性讀snapshot的建立時間點的,只能讀取該時間點之前已經提交的數據。

如何避免髒讀、不可重複讀、幻讀。


1、設置隔離級別
2、悲觀鎖:傳統關係型數據庫的行鎖、表鎖、讀鎖、寫鎖。就是這種鎖機制。 間隙鎖
3、樂觀鎖;使用版本號或則cas算法。
      cas: compare and swap(比較和交換):著名無鎖算法。無鎖編程:不使用鎖的情況下實現多個線程之間的變量同步,也就是在沒有阻塞的情況下實現線程變量同步,
        CAS設計到三個操作:
            讀取需要操作的值V
            進行比較的值A
            虛擬寫入的值B
            當且僅當V的值等於A時,cas將需要寫入的新值B來更新V,否則不執行任何操作。一般情況下是一個自旋操作,不斷重試。
樂觀鎖適用於寫比較少的情況(併發量大/多讀場景)即衝突少發生。這樣可以省去鎖的開銷,加大系統的吞吐量;如果多寫的情況,一般會產生衝突,這樣導致上層應用不斷retry,反而降低了系統性能,所以悲觀鎖比較適合。


mysql innodb引擎的鎖機制(悲觀鎖)


    innodb支持事務支持行鎖,表鎖,myisam不支持事務,只支持表鎖。
1)鎖的使用
共享鎖:允許一個事務去讀一行,阻止其他事務獲得相同數據集的排它鎖。(可以讀不可以寫)
排它鎖:允許獲得鎖事務進行跟新,拒絕其他事務獲得鎖。
意向共享鎖 鎖表 表明加鎖的類型 先對錶加鎖,再決定對具體行還是表加鎖。
意向排他鎖 鎖表 表明枷鎖的類型
注意:意向鎖是數據庫主動加的不需要手動處理。
2)鎖的粒度:行鎖、表鎖、葉鎖(間隙鎖)
行鎖是對索引列或則主鍵加鎖。

通過加鎖控制可以保證數據的一致性,但是同樣一條數據,不能讀寫併發(排它鎖的原因)
這時就要引入 數據多版本控制來實現讀寫併發。


mvcc(樂觀鎖)

這項技術使得InnoDB的事務隔離級別下執行一致性讀操作有了保證,換言之,就是爲了查詢一些正在被另一個事務更新的行,並且可以看到它們被更新之前的值。這是一個可以用來增強併發性的強大的技術,因爲這樣的一來的話查詢就不用等待另一個事務釋放鎖。

 

數據多版本實現的原理是:

1,寫任務發生時,首先複製一份舊數據,以版本號區分

2,寫任務操作新克隆的數據,直至提交

3,併發讀的任務可以繼續從舊數據(快照)讀取數據,不至於堵塞

快照讀 和當前讀 


select是快照讀    其他 u d i  selet..lock in share mode  select for update 當前讀
保證同一個事務中讀到的數據是相同的不會髒讀   grap lock保證不會幻讀

實現原理  innodb加版本號

總結:排它鎖 是 串行執行
        共享鎖 是讀讀併發
        數據多版本:讀寫併發

總結:

數據庫併發問題,主要通過設置事務隔離級別來解決,而事務隔離級別一般則通過鎖機制的實現;

MySQL默認隔離級別(RR)使用MVCC+鎖混合的模式來解決髒讀、不可重讀、幻讀等問題。

MySQL(Innodb引擎)下

默認的事務級別爲:可重複讀級別(RR);(可通過設置進行更改)

默認鎖級別爲:行鎖;(可通過設置進行更改)

Where篩選條件中使用索引字段的,加的是行鎖;不是使用索引字段篩選的,加的是表鎖。

意向共享鎖和意向排它鎖是數據庫主動加的,不需要我們手動處理;

對於UPDATE、DELETE和INSERT語句,InnoDB會自動給涉及數據集加排他鎖;

對於普通SELECT語句,InnoDB不會加任何鎖;(可以自己手動上鎖)


 


 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章