詳解MySQL事務(wù)的隔離級別與MVCC
事務(wù)隔離級別
事務(wù)并發(fā)執(zhí)行遇到的問題
- 臟寫
- 如果一個事務(wù)修改了另一個未提交事務(wù)修改過的數(shù)據(jù),那就意味著發(fā)生了臟寫
- 如果一個事務(wù)修改了另一個未提交事務(wù)修改過的數(shù)據(jù),那就意味著發(fā)生了臟寫
- 臟讀
- 如果一個事務(wù)讀到了另一個未提交事務(wù)修改過的數(shù)據(jù),那就意味著發(fā)生了臟讀
- 如果一個事務(wù)讀到了另一個未提交事務(wù)修改過的數(shù)據(jù),那就意味著發(fā)生了臟讀
- 不可重復(fù)讀
- 如果一個事務(wù)只能讀到另一個已經(jīng)提交的事務(wù)修改過的數(shù)據(jù),并且其他事務(wù)每對該數(shù)據(jù)進(jìn)行一次修改并提交后,該事務(wù)都能查詢到最新值,那就意味著發(fā)生了不可重復(fù)讀
- 如果一個事務(wù)只能讀到另一個已經(jīng)提交的事務(wù)修改過的數(shù)據(jù),并且其他事務(wù)每對該數(shù)據(jù)進(jìn)行一次修改并提交后,該事務(wù)都能查詢到最新值,那就意味著發(fā)生了不可重復(fù)讀
- 幻讀
- 如果一個事務(wù)先根據(jù)某些條件查詢出一些記錄,之后另一個事務(wù)又向表中插入了符合這些條件的記錄,原先的事務(wù)再次按照該條件查詢時,能把另一個事務(wù)插入的記錄也讀出來,那就意味著發(fā)生了幻讀。
- 幻讀強調(diào)的是一個事務(wù)按照某個相同條件多次讀取記錄時,后讀取時讀到了之前沒有讀到的記錄
- 那對于先前已經(jīng)讀到的記錄,之后又讀取不到這種情況,算啥呢?其實這相當(dāng)于對每一條記錄都發(fā)生了不可重復(fù)讀的現(xiàn)象。幻讀只是重點強調(diào)了讀取到了之前讀取沒有獲取到的記錄。
SQL標(biāo)準(zhǔn)中的四種隔離級別
- READ UNCOMMITTED: 未提交讀 臟讀、不可重復(fù)讀、幻讀 發(fā)生
- READ COMMITTED:已提交讀 不可重復(fù)讀、幻讀 發(fā)生
- REPEATBLE READ:可重復(fù)讀 幻讀 發(fā)生
- SERIALIZABLE:可串行化 不發(fā)生
MySQL中支持的四種隔離級別
- MySQL在REPEATABLE READ隔離級別下,是可以禁止幻讀問題的發(fā)生的(關(guān)于如何禁止我們之后會詳細(xì)說明的)
- MySQL默認(rèn)隔離級別為REPEATABLE READ
MVCC原理
版本鏈
對于使用InnoDB存儲引擎的表來說,它的聚簇索引記錄中都包含兩個必要的隱藏列
- trx_id:每次一個事務(wù)對某條聚簇索引記錄進(jìn)行改動時,都會把該事務(wù)的事務(wù)id賦值給trx_id隱藏列
- roll_pointer:每次對某條聚簇索引記錄進(jìn)行改動時,都會把舊的版本寫入到undo日志,然后這個隱藏列就相當(dāng)于一個指針,可以通過它來找到該記錄修改前的信息。
ReadView
- 對于使用READ UNCIMMITTED隔離級別的事務(wù)來說,由于可以讀到未提交事務(wù)修改過的記錄,所以直接讀取記錄的最新版本就好了;
- 對于使用READ COMMITTED 和REPEATABLE READ 隔離級別的事務(wù)來說,都必須保證讀到已經(jīng)提交了的事務(wù)修改過的記錄,也就是說假如另一個事務(wù)已經(jīng)修改了記錄但是尚未提交,是不能直接讀取到最新版本記錄的。核心問題:需要判斷一下版本鏈中的哪個版本是當(dāng)前事務(wù)可見的。為此設(shè)計了readview
- readView包含4個比較重要的內(nèi)容:
- m_ids:表示在生成ReadView時當(dāng)前系統(tǒng)中活躍的讀寫事務(wù)的事務(wù)ID
- min_trx_id:表示生成ReadView時當(dāng)前系統(tǒng)中活躍的讀寫事務(wù)中最小的事務(wù)id,也就是m_ids中的最小值
- max_trx_id: 表示生成ReadView時系統(tǒng)中應(yīng)該分配給下一個事務(wù)的id值
- creator_trx_id:表示生成該ReadView的事務(wù)的事務(wù)ID
- 我們前邊說過,只有在對表中的記錄做改動時(執(zhí)行INSERT、DELETE、UPDATE這些語句時)才會為事務(wù)分配事務(wù)id,否則在一個只讀事務(wù)中的事務(wù)id值都默認(rèn)為0。
- 有了這個ReadView,這樣在訪問某條記錄時,只需要按照下邊的步驟判斷記錄的某個版本是否可見:
- 如果被訪問的版本的trx_id屬性與ReadView中的creator_trx_id相同,意味著當(dāng)前事務(wù)在訪問它自己修改過的記錄,所以該版本可以被當(dāng)前事務(wù)訪問
- 如果被訪問的trx_id屬性值小于ReadView中的min_trx_id值,表明生成該版本的事務(wù)在當(dāng)前事務(wù)生成ReadView時已經(jīng)提交,所以該版本可以被當(dāng)前事務(wù)訪問
- 如果被訪問版本的trx_id屬性值大于或等于ReadView中的max_trx_id值,表明生成該版本的事務(wù)在當(dāng)前事務(wù)生成ReadView后才開啟,所以該版本不可以被當(dāng)前事務(wù)訪問
- 如果被訪問版本的trx_id屬性值在ReadView的min_trx_id和max_trx_id之間,那就需要判斷一下trx_id屬性值是不是在m_ids列表中,如果在,說明創(chuàng)建ReadView時生成該版本的事務(wù)還是活躍的,該版本不可被訪問;如果不在,說明創(chuàng)建ReadView時生成該版本的事務(wù)已經(jīng)被提交,該版本可以被訪問。
總結(jié)一下:
- READ COMMITTED隔離級別的事務(wù)在每次查詢開始時都會生成一個獨立的ReadView
- REPEATABLE READ :在第一次讀取數(shù)據(jù)時生成一個ReadView,也就是說兩次SELECT 查詢得到的結(jié)果是重復(fù)的。
MVCC總結(jié): 所謂的MVCC指的就是在使用 READ COMMITTED 和REPEATABLE READ 這兩種隔離級別的事務(wù)在執(zhí)行普通的SELECT 操作時訪問的記錄的版本鏈的過程,這樣子可以使不用的事務(wù)的讀-寫、寫-讀操作并發(fā)執(zhí)行,從而提升性能。
mysql如何在RR級別解決幻讀的
1.當(dāng)前讀,讀的是最新版本,并且需要獲取對應(yīng)記錄的鎖,如下SQL
- select ... lock in share mode
- select ... for update
- update 、delete 、insert
是通過next-key 來實現(xiàn)幻讀的
2.快照讀 是通過mvcc 來解決的
以上就是詳解MySQL事務(wù)的隔離級別與MVCC的詳細(xì)內(nèi)容,更多關(guān)于MySQL事務(wù)的隔離級別與MVCC的資料請關(guān)注本站其它相關(guān)文章!
版權(quán)聲明:本站文章來源標(biāo)注為YINGSOO的內(nèi)容版權(quán)均為本站所有,歡迎引用、轉(zhuǎn)載,請保持原文完整并注明來源及原文鏈接。禁止復(fù)制或仿造本網(wǎng)站,禁止在非www.sddonglingsh.com所屬的服務(wù)器上建立鏡像,否則將依法追究法律責(zé)任。本站部分內(nèi)容來源于網(wǎng)友推薦、互聯(lián)網(wǎng)收集整理而來,僅供學(xué)習(xí)參考,不代表本站立場,如有內(nèi)容涉嫌侵權(quán),請聯(lián)系alex-e#qq.com處理。