一、事務(wù)隔離級(jí)別
- 事務(wù)是指具有ACID四個(gè)屬性的事務(wù),即原子性、一致性、隔離性和持久性。mysql根據(jù)事務(wù)的控制由弱到強(qiáng)分為多個(gè)隔離級(jí)別,即讀未提交、讀已提交、可重復(fù)讀和序列化讀,innoDb默認(rèn)為可重復(fù)讀級(jí)別。
- 讀未提交:事務(wù)中可以讀到其他事務(wù)還未提交的數(shù)據(jù),即臟讀,相當(dāng)于沒(méi)有做事務(wù)控制。
- 讀已提交:事務(wù)過(guò)程中讀到的數(shù)據(jù)都是事務(wù)開始時(shí)已提交的數(shù)據(jù),但事務(wù)中可能讀到其他事務(wù)提交的更新,也可叫做不可重復(fù)讀。
- 可重復(fù)讀:整個(gè)事務(wù)中讀到的數(shù)據(jù)都和事務(wù)開始時(shí)一樣,不會(huì)讀到事務(wù)中其他事務(wù)提交的更新,但是會(huì)讀到其他事務(wù)添加的行,即幻讀。
- 序列化讀:所有事務(wù)串性化執(zhí)行,不會(huì)出現(xiàn)幻讀。
二、mvvc
- mysql采用mvvc多版本并發(fā)控制技術(shù)支持事務(wù)的不同隔離級(jí)別查詢,且只在讀提交、可重復(fù)讀下有效。讀未提交每次都讀最新數(shù)據(jù),序列化讀通過(guò)加鎖實(shí)現(xiàn)。
- 每次事務(wù)操作后mvvc給每個(gè)記錄行都標(biāo)記了事務(wù)id,相當(dāng)于每個(gè)記錄行的不同版本號(hào)。
- 事務(wù)開始時(shí),會(huì)記錄當(dāng)前已提交的最新一個(gè)事務(wù)id,以及正在進(jìn)行中的事務(wù)。在事務(wù)過(guò)程中讀取數(shù)據(jù)時(shí),根據(jù)記錄的最新事務(wù)id與當(dāng)前數(shù)據(jù)行的事務(wù)id進(jìn)行比較,獲取適當(dāng)版本的數(shù)據(jù),從而實(shí)現(xiàn)不同隔離級(jí)別。
- 例如,在可重復(fù)讀隔離級(jí)別下,事務(wù)開始時(shí)數(shù)據(jù)行a有多個(gè)版本a(v1, v2, v3),以及正在進(jìn)行中的事務(wù)v4。那么當(dāng)前事務(wù)記錄數(shù)據(jù)行a的版本號(hào)為v3,這么事務(wù)進(jìn)行中讀取數(shù)據(jù)行a時(shí),即使事務(wù)v4提交了也會(huì)讀取其v3版本的數(shù)據(jù),從而實(shí)現(xiàn)可重復(fù)讀。
- mvvc通過(guò)版本控制實(shí)現(xiàn)不同隔離級(jí)別,相當(dāng)于為事務(wù)創(chuàng)建了一個(gè)視圖,同時(shí)也不用復(fù)制數(shù)據(jù),不用加鎖,非常高效。
三、mysql鎖
mysql通過(guò)mvvc實(shí)現(xiàn)了一致性讀,但數(shù)據(jù)更新還是需要加鎖來(lái)實(shí)現(xiàn)并發(fā)控制。
- 按照鎖粒度劃分:數(shù)據(jù)庫(kù)鎖、表鎖、行鎖。
- 按照兼容性劃分:共享鎖(讀鎖)、排他鎖(寫鎖)。
- 具體的鎖實(shí)現(xiàn):記錄鎖(行鎖)、間歇鎖(gap)、臨界鎖(next-key)、表鎖、意向鎖(為了兼容表鎖和行鎖)。
- 鎖模式:IS(意向共享鎖)、S(共享鎖)、IX(意向排他鎖)、X(排他鎖)。
不同鎖介紹:https://dev.mysql.com/doc/refman/8.0/en/innodb-locking.html - 加鎖原則,來(lái)自《mysql實(shí)戰(zhàn)45講-林奇》:
1. 加鎖基本單位next-key
2. 查找過(guò)程中訪問(wèn)到的行才需要加鎖
3.唯一索引上的等值查詢,next-key將退化為行鎖
4.索引上的等值查詢,向后遍歷最后一個(gè)不相等的記錄的next-key將退化為間歇鎖。
事務(wù)中數(shù)據(jù)的并發(fā)控制多數(shù)時(shí)候是通過(guò)多個(gè)鎖共同控制完成的。通過(guò)以下命令開啟事務(wù)詳情查詢:
set GLOBAL innodb_status_output_locks=ON;
set GLOBAL innodb_status_output=ON;
show engine innodb status;
查詢鎖和事務(wù):
select from information_schema.innodb_locks;
select from information_schema.innodb_lock_waits;
select * from information_schema.innodb_trx;