什么是事務(wù)瞪慧?
官方點(diǎn)說,事務(wù)就是數(shù)據(jù)庫管理系統(tǒng)執(zhí)行過程中的一個邏輯單位茅主,由一個有限的數(shù)據(jù)庫操作作序列構(gòu)成舞痰。
通俗點(diǎn)說,事務(wù)就是一組操作要么同時成功要么同時失敗诀姚。
事務(wù)的四大特性(ACID):
1.原子性(atomicity):一個事務(wù)必須視為一個不可分割的最小工作單元响牛,整個事務(wù)中的所有操作要么全部提交成功,要么全部失敗回滾,對于一個事務(wù)來說呀打,不可能只執(zhí)行其中的一部分操作矢赁,這就是事務(wù)的原子性。
2.一致性(consistency):數(shù)據(jù)庫總數(shù)從一個一致性的狀態(tài)轉(zhuǎn)換到另一個一致性的狀態(tài)贬丛。
3.隔離性(isolation):一個事務(wù)所做的修改在最終提交以前撩银,對其他事務(wù)是不可見的。
4.持久性(durability):一旦事務(wù)提交瘫寝,則其所做的修改就會永久保存到數(shù)據(jù)庫中蜒蕾。此時即使系統(tǒng)崩潰,修改的數(shù)據(jù)也不會丟失焕阿。
事務(wù)并發(fā)帶來的問題:
1.臟讀:臟讀是指一個事務(wù)在處理過程中讀取了另一個事務(wù)未提交的數(shù)據(jù)咪啡。
2.不可重復(fù)讀:指的是在一個事務(wù)處理中讀取到其他事務(wù)修改或刪除并提交的數(shù)據(jù),導(dǎo)致多次讀取結(jié)果不一致暮屡。
3.幻讀:指的是在一個事務(wù)處理中讀取到其他事務(wù)插入并提交的數(shù)據(jù)撤摸,導(dǎo)致多次讀取結(jié)果不一致。
事務(wù)并發(fā)帶來的三大問題其實(shí)都是數(shù)據(jù)庫讀一致性問題褒纲,必須由數(shù)據(jù)庫提供一定的事務(wù)隔離機(jī)制來解決准夷。
mysql事務(wù)的隔離級別:
事務(wù)隔離級別臟讀不可重復(fù)讀幻讀
讀未提交(read-uncommitted)可能可能可能
不可重復(fù)讀(read-committed)不可能可能可能
可重復(fù)讀(repeatable-read)不可能不可能在InnoDB中不可能
串行化(serializable)不可能不可能不可能
為什么InnoDB中在rr級別的時候就解決了幻讀這個問題?
MVCC與LBCC
如果要解決讀一致性問題莺掠,保證一個事務(wù)前后讀取數(shù)據(jù)結(jié)果的一致性衫嵌,實(shí)現(xiàn)事務(wù)隔離應(yīng)該怎么做?
簡單思考下解決方案:
LBCC(全稱Lock Based Concyrrency Control):在讀取數(shù)據(jù)前對其加鎖彻秆,阻止其他事務(wù)對數(shù)據(jù)進(jìn)行修改楔绞。
MVCC(全稱Multi Version Concurrency Control):生成一個數(shù)據(jù)請求時間點(diǎn)的一致性數(shù)據(jù)快照(snapshot),并用這個快照來提供一定級別(語句級或者事務(wù)級)的一致性讀取。
InnoDB中怎么實(shí)現(xiàn)的MVCC
其實(shí)InnoDB中唇兑,自動為每一行數(shù)據(jù)添加了三個隱藏字段:
DB_ROW_ID(6字節(jié)):行標(biāo)識酒朵,前邊的文章中提到過這個字段。
DB_TRX_ID(6字節(jié)):創(chuàng)建版本號扎附,插入或更新行的最后一個事務(wù)的id蔫耽,自動遞增。
DB_ROLL_PIR(7字節(jié)):刪除版本號留夜,用于回滾匙铡。
主要記住,在InnoDB中碍粥,一個事務(wù)開始時慰枕,會生成一個當(dāng)前時間點(diǎn)的數(shù)據(jù)快照(可以理解為一個副本),只要事務(wù)不結(jié)束即纲,他就只能在這個數(shù)據(jù)快照里查找數(shù)據(jù)具帮,也就是只能查找到創(chuàng)建版本號小于當(dāng)前事務(wù)id的數(shù)據(jù)和刪除版本號大于當(dāng)前事務(wù)id的數(shù)據(jù)(或者是在創(chuàng)建版本號小于當(dāng)前事務(wù)id的前提下,沒有刪除版本號的數(shù)據(jù))
在InnoDB中MVCC和鎖是相互配合使用的,下邊就看看鎖的東西蜂厅。
鎖
表鎖與行鎖的區(qū)別:
鎖定粒度:表鎖 > 行鎖
加鎖效率:表鎖 > 行鎖
沖突概率:表鎖 > 行鎖
并發(fā)性能:表鎖 < 行鎖
注意:MYISAM引擎只支持表鎖匪凡,InnoDB既支持表鎖也支持行鎖
行鎖
共享鎖(Shared Locks):
又稱為讀鎖,簡稱s鎖掘猿,顧名思義共享鎖就是多個事務(wù)對于同一數(shù)據(jù)可以共享一把鎖病游,都能訪問到數(shù)據(jù),但是只能讀不能寫稠通,不然容易造成死鎖衬衬。
加鎖方式:在select語句后加LOCK IN SHARE MODE;
釋鎖方式:commit / rollback
排他鎖(Exclusive Locks):
又稱為寫鎖,簡稱x鎖改橘,排他鎖不能和其他鎖共存滋尉,如一個事務(wù)獲取了一個數(shù)據(jù)行的排他鎖,其他事務(wù)就不能獲取該行的鎖(包括共享鎖和排他鎖)飞主,只有獲取到該行的排他鎖的事務(wù)是可以對數(shù)據(jù)進(jìn)行讀取和修改狮惜。
加鎖方式:
1.手動加鎖:在語句后加FOR UPDATE;(select時需手動)
2.自動加鎖:在insert / update / delete 語句時或自動加排他鎖
釋鎖方式:commit / rollback
注意:innoDB中獲取鎖超時間默認(rèn)為50秒,查看語句為show variables like 'innodb_lock_wait_timeout';碌识。
表鎖
意向共享鎖(Intention Shared Lock):
簡稱IS鎖碾篡,表示事務(wù)準(zhǔn)備給數(shù)據(jù)行進(jìn)行加入共享鎖,也就是說一個數(shù)據(jù)行加共享鎖之前必須先獲得該表的IS鎖筏餐。
意向排他鎖(Intention Exclusive Lock):
簡稱IX鎖开泽,表示事務(wù)準(zhǔn)備給數(shù)據(jù)行加入排他鎖,說明事務(wù)在一個數(shù)據(jù)行加排他鎖前必須要獲得該表的IX鎖魁瞪。
注意:意向鎖是由數(shù)據(jù)引擎自己維護(hù)的穆律,用戶是無法手動操作意向鎖的。
問:為什么需要表級別的意向鎖佩番?
答:假如要給一張表加表鎖众旗,那么這個加表鎖的前提是沒有其他任何事務(wù)已經(jīng)鎖住了這張表的任意一行數(shù)據(jù)罢杉。那么怎么來確定這個前提呢趟畏?
必然要有一個全表掃描才能確定每一行數(shù)據(jù)都沒有被加鎖。如果這張表數(shù)據(jù)比較大呢滩租?有千萬數(shù)據(jù)赋秀,那這個全表掃描的過程就要消耗很多時間才能確定能不能加表鎖,并且如果在掃描的過程中萬一有其他事務(wù)來加鎖怎么辦律想,所以說猎莲,全表掃描加表鎖這種方式既慢并且消耗性能,帶來嚴(yán)重的并發(fā)問題技即。為了解決這一問題著洼,才有了意向鎖。加表鎖的時候只要看一下這個表上有沒有意向鎖就ok了,因?yàn)槟慵有墟i的時候都會先獲取到表的意向鎖才行身笤。意向鎖就是為了提高加表鎖的效率豹悬。
問:鎖的作用?
答:和Java中的鎖一樣液荸,都是為了解決資源競爭的問題瞻佛,Java中的資源是對象。而數(shù)據(jù)庫中的資源就是表和行數(shù)據(jù)娇钱,鎖就是為了解決對于事務(wù)并發(fā)訪問的問題伤柄。
問:鎖到底鎖住了什么?是一行數(shù)據(jù)文搂?還是一個字段适刀?
InnoDB行鎖原理
針對上述問題,其實(shí)有三種情況:
1.沒有索引的表上加行鎖细疚,會鎖住整張表蔗彤,出現(xiàn)鎖表的情況。
2.有主鍵索引的表上加行鎖疯兼,會鎖住索引然遏。
3.用唯一索引(輔助索引)的字段加行鎖,會鎖住輔助索引和主鍵索引吧彪。
在InnoDB中待侵,行鎖就是鎖住索引記錄來實(shí)現(xiàn)的,加鎖的時候在mysql自帶的information_schema庫中的INN0DB_LOCKS表中可以看到:
lock_type:鎖類型姨裸;lock_table:加鎖的表秧倾;lock_index:鎖住的索引
問:沒有索引的表上加行鎖為什么會鎖表?
注意:在之前的文章中說過傀缩,InnoDB中一張表是不可能沒有索引的那先,如果用戶沒定義,默認(rèn)就會用隱藏的字段ROW_ID作為聚集索引赡艰。那么加行鎖的時候售淡,條件沒有命中索引(因?yàn)橛玫氖请[藏字段作為索引,那么肯定不能命中翱犊濉)揖闸,就會走全表掃描,不得不把所有的聚集索引全都鎖住料身,所以會出現(xiàn)鎖表的情況汤纸。
問:為什么用唯一索引加行鎖時,會鎖住主鍵索引芹血?
這就又涉及到前邊的知識——回表贮泞,因?yàn)镮nnoDB中楞慈,輔助索引的葉子節(jié)點(diǎn)上存放的是主鍵索引,索引用輔助索引加行鎖時會先鎖住輔助索引啃擦,然后鎖住主鍵索引抖部。本質(zhì)上其實(shí)還是鎖住了主鍵索引。那么鎖到底鎖住了什么议惰?相信大家心里已經(jīng)有答案了慎颗。
InnoDB行鎖算法
主要有三種算法:
1.記錄鎖(Record Lock)
鎖定記錄,在唯一性索引(唯一/主鍵)等值查詢時言询,精準(zhǔn)匹配到一個索引記錄的時候會使用記錄鎖俯萎。
(如下圖)比如用where id = 4這個條件時,就會精準(zhǔn)匹配到一個索引記錄运杭。
2.間隙鎖(Gap Lock)
鎖定區(qū)間夫啊,鎖定是的是數(shù)據(jù)庫不存在的區(qū)間范圍,Gap Lock相互之間不會沖突辆憔。
比如撇眯,用where id = 6這個條件時,發(fā)現(xiàn)沒有這個數(shù)據(jù)記錄虱咧,就會鎖仔荛弧(4,7)這個范圍(注意左開右開不包括記錄)腕巡。
3.臨建鎖(Next-key Lock)
鎖定記錄和區(qū)間玄坦,條件范圍查找時,會鎖住記錄和區(qū)間绘沉。Next-key Lock = Record Lock + Gap Lock煎楣;
比如,用where id > 5 and id <9時车伞,就會鎖自穸(4,7]和(7,10]這個范圍的區(qū)間和記錄另玖,也就是(4,10]這個范圍的記錄和區(qū)間(注意是左開右閉困曙,不包括左邊的記錄,包括右邊的記錄)
事務(wù)隔離級別的選擇
首先Read Uncommited不加鎖和Serializable串行化這兩種級別基本是不會被使用的日矫。
Read Commited 對于普通的select語句使用mvcc赂弓,對于加鎖的select和更新語句使用Record Lock記錄鎖绑榴。
Repeatable Read 對于普通的select語句使用mvcc哪轿,對于加鎖的select和更新語句會使用Next-key Lock 、 Record Lock 翔怎、 Gap Lock窃诉。
1.RR的間隙鎖會導(dǎo)致鎖的范圍擴(kuò)大
2.條件列未使用到索引時杨耙,RR鎖表,RC鎖行
3.RC的“半一致性”讀(semi-consistent)可以增加update操作的并發(fā)性飘痛。
從上述三個問題中珊膜,看似RC的級別更有優(yōu)勢,其實(shí)在實(shí)際中宣脉,合理的加鎖车柠,在有索引的列加鎖并不會出現(xiàn)上邊的情況,通常使用默認(rèn)的事務(wù)隔離級別RR就ok了塑猖。