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 可重複讀否...