MySQL中?LBCC?和?MVCC?的理解及常見(jiàn)問(wèn)題示例
1. 事務(wù)
介紹MVCC之前,先介紹下事務(wù):事務(wù)是為了保證數(shù)據(jù)庫(kù)中數(shù)據(jù)的完整性和一致性。
事務(wù)的4個(gè)基本要素:
- 原子性(Atomicity):要么同時(shí)成功,要么同時(shí)失敗。(通過(guò)undo log回滾日志實(shí)現(xiàn))
- 一致性(Consistency):一方扣款 xxx 元,另一方收款 xxx 元,符合事物發(fā)展的正常邏輯(通過(guò)lock鎖實(shí)現(xiàn))
- 隔離性(Isolation):此時(shí)有多個(gè)類(lèi)似 扣款/收款 事件同時(shí)發(fā)生,每個(gè)事件之間是相互獨(dú)立的(通過(guò) lock鎖 + MVCC實(shí)現(xiàn))
- 持久性(Durability):不管數(shù)據(jù)庫(kù)宕機(jī)或重啟,數(shù)據(jù)最終都落到了磁盤(pán)上,下次加載依然可見(jiàn) (通過(guò) redo log實(shí)現(xiàn))
2. MVCC初探
目的:主要是為了 提高數(shù)據(jù)庫(kù)并發(fā)性能。用更好的方式去處理 讀/寫(xiě) 沖突,做到即使有 讀/寫(xiě) 沖突時(shí),也能做到不加鎖,非阻塞并發(fā)讀。
不同隔離級(jí)別下,可能引發(fā)的問(wèn)題: 臟讀:并發(fā)情況下,一方事務(wù)讀到了另一方事務(wù) “已 update
但未 commit
” 的數(shù)據(jù),破壞了事務(wù)隔離性。不可重復(fù)讀:并發(fā)情況下,一方事務(wù)讀到了另一方事務(wù) “已 update
或 delete
,并 commit
” 的數(shù)據(jù),破壞了事務(wù)隔離性。幻讀:并發(fā)情況下,一方事務(wù)讀到了另一方事務(wù)" insert
并 commit
"的數(shù)據(jù),導(dǎo)致前后讀取結(jié)果不一致。
MVCC中的四種事務(wù)隔離級(jí)別:
提問(wèn):V1、V2、V3在不同事務(wù)隔離級(jí)別下讀取到的值分別是:
- RU-讀未提交 級(jí)別:20、20、20(可能發(fā)生:臟讀、不可重復(fù)讀)
- RC-讀已提交 級(jí)別:18、20、20(不可能發(fā)生:臟讀、可能發(fā)生:不可重復(fù)度)
- RR-可重復(fù)讀 級(jí)別:18、18、20 (不可能發(fā)生:臟讀、不可重復(fù)讀;但是因?yàn)槭聞?wù)A已提交,所以V3再次查詢(xún)時(shí)跟事務(wù)A是沒(méi)有隔離性的要求的,因此V3讀取到的是20)
3. LBCC & MVCC
- LBCC(Lock-Base Concurrency Control)基于鎖的并發(fā)控制;
- MVCC(Multiversion Concurrency Control)多版本并發(fā)控制;
LBCC 鎖相關(guān):
MySQL 5.5 版本之前,默認(rèn)的存儲(chǔ)引擎是MyISAM,5.5之后默認(rèn)引擎是Innodb。Innodb支持事務(wù),包括:行鎖/表鎖,MyISAM不支持。 意向鎖 意向共享鎖/讀鎖(表鎖類(lèi)型,無(wú)法手動(dòng)創(chuàng)建),mysql 中語(yǔ)法: lock in share mode
意向排它鎖/寫(xiě)鎖(表鎖類(lèi)型,無(wú)法手動(dòng)創(chuàng)建),mysql 中語(yǔ)法: for update
常見(jiàn)問(wèn)題:為什么要加入意向鎖?
意向鎖并不是真正用來(lái)鎖定數(shù)據(jù)的,而是用來(lái)告訴你當(dāng)前表中是否已經(jīng)有了被 共享鎖/排它鎖
鎖定的數(shù)據(jù)行。如果有就沒(méi)必要再去加無(wú)用的表鎖了,起到一個(gè)標(biāo)識(shí)作用,提高加表鎖的效率(相當(dāng)于高鐵洗手間門(mén)上方是否有人正在使用的 “指示燈”)。
記錄鎖(Record Lock)、間隙鎖(Gap Lock)、臨鍵鎖(Next-Key Lock):
- 介紹:臨鍵鎖 = 記錄鎖 + 間隙鎖,是 RR 可重復(fù)讀-隔離級(jí)別下獨(dú)有的,
- 目的:間隙鎖的出現(xiàn)就是為了解決可重復(fù)讀隔離級(jí)別下的幻讀問(wèn)題
問(wèn)題:如圖示:執(zhí)行此sql語(yǔ)句(先開(kāi)啟事務(wù)):BEGIN; SELECT * FROM tbl WHERE id > 15 FOR UPDATE;
,以下兩個(gè)sql語(yǔ)句可以執(zhí)行成功嗎?
MVCC底層實(shí)現(xiàn)詳解:
快照讀(實(shí)際上為讀相關(guān)的操作):讀取的是記錄的可見(jiàn)版本 (有可能是歷史版本),不用加鎖。
簡(jiǎn)單的 SELECT
操作,屬于快照讀,不加鎖。
SELECT * FROM user WHERE ?
當(dāng)前讀(實(shí)際上為寫(xiě)相關(guān)的操作):在事務(wù)中,update
數(shù)據(jù)前,還要去MySQL中重新讀取一遍該數(shù)據(jù)對(duì)應(yīng)最新版本的記錄,并且 當(dāng)前讀 返回的記錄都會(huì)加上鎖,保證其他事務(wù)不會(huì)再并發(fā)修改這條記錄。以下兩種方式都屬于當(dāng)前讀,需要加鎖:
- 特殊讀 (加鎖讀):
SELECT
* FROM user WHERE id = xxxLOCK IN SHARE MODE
; INSERT
/UPDATE
/DELETE
等寫(xiě)操作。
問(wèn)題:在 RR-可重復(fù)讀 的默認(rèn)隔離級(jí)別下,假設(shè)起始的age為18,那么Q1和Q2對(duì)應(yīng)的age分別是多少呢?
- 針對(duì) “事務(wù)B” 分析:因?yàn)榇嬖?
UPDATE
寫(xiě)操作,觸發(fā)了 當(dāng)前讀,所以要先去讀最新提交的版本號(hào)記錄(即:事務(wù)CUPDATE
后提交的記錄),然后事務(wù)B再去執(zhí)行自己的UPDATE
操作。也就是要先去讀事務(wù)C提交的最新數(shù)據(jù)為19,然后事務(wù)B自身再UPDATE
加1最終變?yōu)?0。 - 針對(duì) “事務(wù)A” 分析:因?yàn)槭聞?wù)A本身是沒(méi)有任何的寫(xiě)操作,僅僅是
SELECT
查詢(xún)操作,觸發(fā) 快照讀。所以事務(wù)A只認(rèn)準(zhǔn)事務(wù) BEGIN 開(kāi)始之前記錄的 最新最后提交的版本號(hào),其記錄值也就是初始的18。
BEGIN
事務(wù)開(kāi)始的時(shí)候會(huì)創(chuàng)建一個(gè)快照,并為對(duì)應(yīng)事務(wù)分配一個(gè)事務(wù)id,即 TRX_ID- 開(kāi)啟事務(wù)之前最后的版本號(hào)為:up_limit_id=999,對(duì)應(yīng) age=18
- 事務(wù)B和事務(wù)C都有
UPDATE
操作(當(dāng)前讀),所以 row_trx_id 為自身的 TRX_ID 的值,分別是1001和1002。而事務(wù)A沒(méi)有UPDATE
操作(快照讀),所以只認(rèn)準(zhǔn)事務(wù)A在 事務(wù)開(kāi)始前 最后的版本號(hào) up_limit_id=999,其 age=18。
總結(jié)
- 事務(wù):是為了保證數(shù)據(jù)庫(kù)中數(shù)據(jù)的完整性和一致性。事務(wù)的4個(gè)特性:ACID。
- MVCC的好處:提高數(shù)據(jù)庫(kù)并發(fā)性能。用更好的方式去處理 讀/寫(xiě) 沖突,做到即使有 讀/寫(xiě) 沖突時(shí),也能做到不加鎖,非阻塞并發(fā)讀。
- MVCC四種隔離級(jí)別 :讀未提交、讀已提交、可重復(fù)讀(MySQL默認(rèn)級(jí)別)、串行化。
- MVCC事務(wù)隔離級(jí)別中,常見(jiàn)的三種問(wèn)題:臟讀、幻讀、不可重復(fù)讀。在RR的默認(rèn)隔離級(jí)別下,單純的
SELECT
只觸發(fā) “快照讀” 。而當(dāng)你包含INSERT
/UPDATE
/DELETE
等 寫(xiě)操作 時(shí),這時(shí)就會(huì)觸發(fā) 當(dāng)前讀,也就是在事務(wù)中,在相關(guān)寫(xiě)操作之前會(huì)再去讀取一次其他事務(wù)的最后提交記錄。這里的關(guān)鍵在于你事務(wù)中的sql是單純的SELECT
語(yǔ)句(快照讀),還是你事務(wù)在的sql是包含了INSERT
/UPDATE
/DELETE
等 寫(xiě)操作(當(dāng)前讀)。 - 沒(méi)有建立索引或索引失效,行鎖會(huì)升級(jí)為表鎖,因?yàn)檎也坏綄?duì)應(yīng)行記錄。所以為了避免兩個(gè)事務(wù)同時(shí)修改一張表的不同記錄會(huì)導(dǎo)致表鎖的問(wèn)題,建議加上索引,這樣就只是行鎖,而不會(huì)升級(jí)為表鎖!
- 幻讀的解決關(guān)鍵在于 間隙鎖 和 臨鍵鎖(臨鍵鎖 = 記錄鎖 + 間隙鎖) 。
最后,補(bǔ)充一個(gè)問(wèn)題點(diǎn):
如果不聲明的創(chuàng)建主鍵,會(huì)有哪些危害? 比如你的id(假設(shè)int類(lèi)型)沒(méi)有聲明為主鍵,并且也沒(méi)有聲明唯一索引(當(dāng)未聲明主鍵時(shí),唯一索引會(huì)被取代為主鍵)
- 行鎖升級(jí)為表鎖
- 當(dāng)數(shù)據(jù)量達(dá)到頂峰的時(shí)候,可能會(huì)造成“主鍵沖突”,int的取值范圍為2^32 -1,當(dāng)未聲明主鍵時(shí),達(dá)到最大值范圍時(shí),id會(huì)再次重新從0開(kāi)使自增,這時(shí)候可能會(huì)出現(xiàn)覆蓋之前row_id記錄的情況,造成數(shù)據(jù)丟失。相反的,如果聲明主鍵的話(huà),那么當(dāng)id達(dá)到上限時(shí),再次insert時(shí)會(huì)報(bào)“主鍵沖突”錯(cuò)誤,這時(shí)候可以將之前的int 類(lèi)型的id改為big int。
- MySQL會(huì)自動(dòng)聲明一個(gè)“隱藏主鍵 row_id”,占6字節(jié)。而你自己聲明int類(lèi)型的主鍵時(shí),只會(huì)消耗4字節(jié)。因此這是一種資源的浪費(fèi)!
到此這篇關(guān)于MySQL中 LBCC 和 MVCC 的理解及常見(jiàn)問(wèn)題示例的文章就介紹到這了,更多相關(guān)MySQL中LBCC和 MVCC內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
mysql 批量查詢(xún)?nèi)∶恳唤M最新一條數(shù)據(jù)
根據(jù)車(chē)牌號(hào)查詢(xún)最新的一條交車(chē)記錄的‘合同號(hào)’ ,這里只需要查詢(xún)‘合同號(hào)’這個(gè)字段,這篇文章主要介紹了mysql 批量查詢(xún)?nèi)∶恳唤M最新一條數(shù)據(jù),需要的朋友可以參考下2024-02-02mysql如何通過(guò)my.ini更改默認(rèn)字符集
這篇文章主要介紹了mysql如何通過(guò)my.ini更改默認(rèn)字符集問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-12-12安全快速修改Mysql數(shù)據(jù)庫(kù)名的5種方法
mysql中如何重命名數(shù)據(jù)庫(kù)?這篇文章主要介紹了安全快速修改Mysql數(shù)據(jù)庫(kù)名的5種方法,需要的朋友可以參考下2014-04-04MySQL?從0到1打開(kāi)數(shù)據(jù)庫(kù)管理操作方法
數(shù)據(jù)庫(kù)管理系統(tǒng)(DataBase?Management?System)是用來(lái)創(chuàng)建數(shù)據(jù)庫(kù)和管理數(shù)據(jù)庫(kù)數(shù)據(jù)的一個(gè)管理軟件,我們口頭說(shuō)的MySQL數(shù)據(jù)庫(kù)就是這個(gè)管理系統(tǒng),這篇文章主要介紹了MySQL從0到1打開(kāi)數(shù)據(jù)庫(kù)管理,需要的朋友可以參考下2023-06-06解決Mysql服務(wù)器啟動(dòng)時(shí)報(bào)錯(cuò)問(wèn)題的方法
這篇文章主要介紹了解決Mysql服務(wù)器啟動(dòng)時(shí)報(bào)錯(cuò)問(wèn)題的方法,需要的朋友可以參考下2015-11-11MySQL?中的count(*)?與?count(1)?誰(shuí)更快一些?
這篇文章主要討論MySQL?中?count(*)?與?count(1)?誰(shuí)更快一些?以下討論基于?InnoDB?存儲(chǔ)引擎,并且再文末單獨(dú)說(shuō)一下MyISAM?,感興趣的小伙伴可以參考一下2022-02-02