Mysql資料庫事務的隔離級別和鎖的實現原理分析

2021-07-16 21:41:54 字數 3862 閱讀 3773

mysql資料庫的事務隔離級別相信很多同學都知道.

大家有沒有想過它是如何實現的呢?帶著這些問題我翻閱了相關資料庫的書籍和資料,把我的理解寫下來.

mysql資料庫的隔離界別如下:

事務中的修改,即使沒有提交,對其它事務也是可見的.  髒讀(dirty read).

乙個事務開始時,只能"看見"已經提交的事務所做的修改. 這個級別有時候也叫不可重複讀(nonrepeatable read).

該級別保證了同一事務中多次讀取到的同樣記錄的結果是一致的. 但理論上,該事務級別還是無法解決另外乙個幻讀的問題(phantom read). 

幻讀:  當某個事務讀取某個範圍內的記錄時,另外乙個事務又在該範圍內插入了新的記錄.當之前的事務再次讀取該範圍時,會產生幻行.(phantom row).

幻讀的問題理應由更高的隔離級別來解決,但mysql和其它資料不一樣,它同樣在可重複讀的隔離級別解決了這個問題. 

也就是說, mysql的可重複讀的隔離級別解決了   "不可重複讀" 和 「幻讀」 2個問題. 稍後我們可以看見它是如何解決的.

而oracle資料庫,可能需要在 「serializable 」 事務隔離級別下才能解決 幻讀問題.

mysql預設的隔離級別也是: repeatable read(可重複讀)

強制事務序列執行,避免了上面說到的 髒讀,不可重複讀,幻讀 三個的問題.

mvcc的實現,是通過儲存資料在某個時間點的快照來實現的. 

innodb的mvcc是通過在每行記錄後面儲存2個隱藏的列來實現的,一列儲存了行的建立時間,一列儲存了行的過期時間(或刪除時間).但它們都儲存的是系統版本號

mvcc最大的作用是:  實現了非阻塞的讀操作,寫操作也只鎖定了必要的行.

mysql的mvcc 只在 read committed  和 repeatable read  2個隔離級別下工作. 

在mvcc的機制下,mysql innodb(預設隔離級別)的增刪改查變成了如下模式:

select:  

1, innodb只查詢版本早於當前事務版本的資料行(行的系統版本號小於等於事務的系統版本號)

2, 行的刪除號要麼未定義,要麼大於當前事務版本號,這樣可以確保事務讀取到的行,在事務開始之前未被刪除.

insert:

innodb 為新插入的每一行儲存當前系統版本號做為行版本號。

delete: 

innodb 為刪除的每一行儲存當前系統版本號作為行刪除標識

update:

innodb 為插入的每一行新記錄,儲存當前系統版本號作為行版本號,同時儲存當前系統版本號到原來的行作為行刪除標識.

注意:  上面的讀取方式只在innodb預設隔離級別下工作,其它的隔離級別會有很大的差異,稍後會看到.

innodb的鎖大致分為:

支援併發高,帶來最大的鎖開銷. 在儲存引擎層實現,伺服器感知不到

伺服器會為諸如: alter table 之類的語句使用表鎖,忽略儲存引擎的鎖機制

但鎖的型別又分為:

(1). 共享鎖(s lock) , 允許事務讀取一行資料

(2). 排他鎖(x lock),允許事務刪除或更新一行資料.

innodb還實現了一種鎖,叫意向鎖(intention lock).意向鎖是將鎖定的物件分為多個層次.

意向鎖的型別分為:

(1). 意向共享鎖(is lock) 

(2). 意向排他鎖(ix lock)

比如:  需要對頁上的記錄加x鎖,那麼需要分別對 資料庫a,表,頁 上加意向鎖ix,最後對記錄r上加x鎖.

一旦對資料庫a,表,頁上加ix鎖失敗,則阻塞.

是innodb儲存引擎下的讀取資料的方式( read committed  和 repeatable read).

一致性非鎖定讀,我的理解是它的讀取方式是把: 事務隔離級別,mvcc,innodb鎖結合起來運用到實現 mysql讀的一種方式.

我們前面提到,mysql讀取資料的方式是讀mvcc下的快照資料.

具體來說, 讀取mysql資料庫時,如果讀取的行正在執行delete,update等操作,這時,讀取操作不會因此去等待行上的x鎖釋放,相反,innodb會讀取行的乙個快照資料.

這樣利用mvcc,innodb實現了非阻塞讀的實現.極大的提高了資料庫的併發性.

但在不同的事務隔離級別下讀取的資料的方式也不一樣: 

(1).  在read committed隔離級別下: 

一致性非鎖定讀總是讀取被鎖定行的最新乙份快照資料.  產生了不可重複讀的問題.

(2).  在repeatable read 事務隔離級別下:

一致性非鎖定讀總是讀取事務開始時的行資料版本.  解決不可重複讀的問題

還有一種讀的方式叫:  一致性鎖定讀(加鎖的讀).

1). select .... for update. 加x鎖

2). select .... lock in share mode. 加s鎖

上面所說的鎖定的物件均為: 索引記錄. 如果innodb儲存引擎在建立的時候沒有設定任何乙個索引,那麼這時,innodb儲存引擎會使用隱式的主鍵進行鎖定.

當查詢的索引含有唯一屬性時,innodb儲存引擎會對next-key lock進行優化,降級為record lock.

下面的2句話是innodb在不同隔離級別下產生"不可重複讀" 和 "幻讀" 和解決它 的根本原因: 

innodb儲存引擎預設的事務隔離級別(repeatable read)下,採用的是 next-key locking的方式來加鎖.

read committed隔離級別下採用的是: record lock 的方式來加鎖.

下面我們來看下 next-key lock的具體實現:

預設儲存引擎下, 比如表a 上的id欄位有索引abc, 並且id有 3,8,12,20這幾個值,那麼該索引可能被next-key locking區間為:

(負無窮,3)

[3,8)

[8,12),

[12,20),

[20,正無窮)

當事務t1鎖定了 [8,12),[12,20)這2個區間時,當插入15時,上面的區間變成:

[8,12),[12,15),[15,20). 

但查詢索引含有唯一屬性時,next-key lock 降級為 record lock,僅鎖住索引本身. 

好,現在表a的id值變成了: 3,8,12,15,20

如果執行下列語句: 

select * from a where id>16 for update. 

innodb會對(16,正無窮) 加鎖,

但在 read committed的事務隔離級別下,因為採用record lock,只會鎖定20這個值.

如果在此時另外乙個事務t2,插入了22這個值,此時,  read committed 隔離級別下就會產生"幻讀"的問題.

但在innodb預設儲存引擎下的next-key lock 模式下,22是插入是會被阻塞的,直到事務t1提交後,釋放x鎖,才能提交22這值.這樣,innodb就這樣解決了幻讀的問題.

現在,我們應該清楚的知道,在不同的事務隔離級別下,mysql innodb是如何實現解決 "不可重複讀" 和 「幻讀」 的問題了吧.

1, innodb用mvcc來實現非阻塞的讀操作,不同隔離級別下,mvcc通過讀取不同版本的資料來解決"不可重複讀" 的問題.

2, innodb的預設隔離級別解決2個問題,"不可重複讀" 和 "幻讀", oracle需要在序列讀中解決"幻讀"問題. innodb的實現方式和一般隔離級別的定義不一致.

3,innodb的預設隔離級別採用next-key lock(間隙鎖) 來解決幻讀問題. 而 read committed隔離級別採用record鎖,因此會產生"幻讀"問題.

4, innodb的儲存引擎不存在鎖公升級的問題(太多的行鎖公升級為表鎖),來降低鎖的開銷. 因為不是根據記錄來產生行鎖的,根據頁對鎖進行管理.

mysql資料庫事務隔離級別

1修改事務隔離級別 全域性修改 修改mysql.ini配置檔案 mysqlid transaction isolation repeatble read 對當前session修改 登入mysql客戶端後,執行命令set session transaction isolation level read...

MYSQL 資料庫 事務 隔離級別

定義 在資料庫 中,為了有效保證併發讀取資料的正確性,提出的事務隔離級別,由低到高依次為 1 read uncommitted 未授權讀取 讀未提交 2 read committed 授權讀取 讀提交 3 repeatable read 可重複讀取 4 serializable 序列化 這四個事務隔...

MySQL資料庫事務隔離級別

一 資料庫事務併發訪問引發的問題 二 mysql資料庫的四種事務隔離級別 隔離級別 名稱髒讀 不可重複讀 幻讀資料庫預設級別 read uncommitted 讀未提交是是 是read committed 讀已提交否是 是oracle sql server repeatable read 可重複讀否...