一.數(shù)據(jù)庫鎖
1.MyISAM和InnoDB在鎖方面的區(qū)別是什么?

MyISAM在對數(shù)據(jù)進行select的時候,他會自動為數(shù)據(jù)庫加上一個表級別的讀鎖,而當我們在對數(shù)據(jù)進行增刪改的時候,會對我們操作的表加上一個寫鎖,當讀鎖未被釋放的時候另位一個session想對表加上寫鎖,就會被阻塞,直到讀鎖被釋放,寫鎖才可以加上。
//顯示的給表加上讀鎖或者寫鎖
lock tables 表名 read/writer
//釋放所有的鎖
unlock tables;
讀鎖又稱為共享鎖:在進行范圍查詢的時候,依然可以對表里面的數(shù)據(jù)進行讀操作,所以讀鎖又稱為共享鎖
-- 添加一個讀鎖,如果在本次會話當中不釋放,其他會話如果進行增刪改(查找仍然可以,所以讀鎖又叫共享鎖)就不能進行,因為會被鎖住
-- 顯示的添加的鎖,必須要顯示的釋放(在哪個會話當中添加的鎖,就要在哪個會話當中釋放,在其他會話當中釋放是無效的)
-- 之所以要顯示的添加鎖,這是因為我們表里面的數(shù)據(jù)太少了,如果數(shù)據(jù)很多,幾千萬條數(shù)據(jù),當我們一個Session當中進行讀操作,另一個Session進行讀操作可以,但是增刪改操作會被阻塞,直到讀操作完畢才開始增刪改操作。
lock tables t_user read;
select * from t_user ;
unlock tables;
無論是表級鎖還是行級鎖都包括共享鎖和排他鎖
寫鎖又稱為排它鎖,當一個Session加上寫鎖以后,另一個Session進行增刪改查都不可以,因為寫鎖又稱為排他鎖,當有寫鎖時,其他操作都不能進行。
-- 讀操作添加排他鎖,當該語句執(zhí)行完畢后,其他會話當中的操作才可以執(zhí)行
-- MyISAM默認支持表級鎖,不支持行級鎖,表級鎖會鎖住整張表,比如我們在查詢1--200w之間的數(shù)據(jù)的同時,更新201w條數(shù)據(jù)仍然會被鎖住,如果是行級鎖,我們就可以更新第201w條數(shù)據(jù)。
select * from t_user for update;
鎖按照級別可以分為共享鎖和排他鎖
上了共享鎖以后,仍然可以上共享鎖,不能上排他鎖
上了排他鎖之后就不能上其他鎖。
InnoDB支持事物,MySQL默認是自動提交事物的,MyISAM不支持事物,”InnoDB使用的是二段鎖(二段鎖協(xié)議)“,即加鎖和解鎖是分成2個階段進行的,即先對同一批事物里的一批操作分別進行加鎖,然后在提交以后對添加的鎖進行統(tǒng)一的解鎖,而當前commit是自動提交的,所以看上去和MyISAM一樣。未commit之前說明鎖還未被釋放.
Set autocommit=0;
-- 添加共享鎖
select * from t_user where id=21037 lock in share mode;
Commit;
即:開啟事物的時候進行加鎖,在commit/rollback之前都是加鎖階段,commit/rollback之后財貨釋放鎖
2.表級鎖
用到表級鎖的時候,當我們只要操作到表里面的數(shù)據(jù)的時候都會觸發(fā)表級鎖,所以表級鎖與索引無關(guān),
當不走索引的時候,整張表都會被鎖住,InnoDB在SQL沒用用到索引的時候走的是表級鎖,在SQL用到索引的時候走的是行級鎖以及GAP鎖
鎖的粒度越細代價越高,相比表級鎖在表的頭部直接加鎖來講,行級鎖還要在掃描到某行的時候?qū)ζ渖湘i,代價是比較大的,InnoDB在支持事物的同時,相比MyISAM帶來了更大的開銷,InnoDB必須要有且僅有一個聚集索引,數(shù)據(jù)文件是和索引綁定在一塊的



3.樂觀鎖和悲觀鎖
樂觀鎖與悲觀鎖不是指具體的什么類型的鎖,而是指看待并發(fā)同步的角度。
悲觀鎖:因此對于同一個數(shù)據(jù)的并發(fā)操作,悲觀鎖采取加鎖的形式。悲觀的認為,不加鎖的并發(fā)操作一定會出問題。他是全程使用排他鎖來實悲觀鎖的。
樂觀鎖:樂觀鎖則認為對于同一個數(shù)據(jù)的并發(fā)操作,是不會發(fā)生修改的。在更新數(shù)據(jù)的時候,會采用嘗試更新,不斷重新的方式更新數(shù)據(jù)。樂觀的認為,不加鎖的并發(fā)操作是沒有事情的。
4.封鎖
所謂的封鎖就是傳統(tǒng)意義上的加鎖,它是實現(xiàn)并發(fā)控制的一種重要手段,即讀之前加讀鎖,寫之前加寫鎖,
5.死鎖活鎖
活鎖:如果事物T1對數(shù)據(jù)R加鎖了,事物T2又請求對事物進行加鎖,于是事物T2等待,此時事物T3也請求對數(shù)據(jù)R加鎖,事物T1釋放鎖之后系統(tǒng)首先批準了事物T3的請求,于是T2繼續(xù)等待,此時事物T4也請求對數(shù)據(jù)R加鎖,事物T3釋放鎖之后系統(tǒng)首先批準了事物T4的請求,于是T2繼續(xù)等待,T2可能永遠的等下去,這就是活鎖。
避免活鎖的簡單方法是:先到先服務(wù)的原則,當多個事物請求同一事物對象時,系統(tǒng)按照先后次序?qū)ζ溥M行排序,越早請求的越先獲得請求鎖
死鎖:事物T1對數(shù)據(jù)R1加鎖,事物T2對數(shù)據(jù)R2加鎖;同時事物T1請求數(shù)據(jù)R2,因為R2已經(jīng)加鎖,所以T1只能等待,同時事物T2請求請求數(shù)據(jù)R1,但由于R1已經(jīng)加鎖,所以T2只能等待。由于他們相互等待,所以T1,T2兩個事物可能永遠也不會結(jié)果,于是形成了死鎖。
6.死鎖的解決辦法
在數(shù)據(jù)庫當中產(chǎn)生死鎖的原因是:2個或者多個事物已經(jīng)對一些數(shù)據(jù)加鎖,但是還想要訪問被其他事物加鎖的對象,從而出現(xiàn)死鎖。
預(yù)防死鎖的方法通常有以下的2種:
1.一次封鎖法:
它要求事物對要訪問的數(shù)據(jù)必須要一次性全部加鎖,否則就會執(zhí)行不下去,一次加鎖法雖然可以有效的防止死鎖,但是增加了鎖的粒度,從而降低了系統(tǒng)的并發(fā)性,而且數(shù)據(jù)庫是不斷變化的,事先很難準確的確定每個事物所需要的數(shù)據(jù),所以只能擴大封鎖范圍,將事物在執(zhí)行過程當中可能需要的數(shù)據(jù)全部進行加鎖,這進一步降低了數(shù)據(jù)的并發(fā)度
2.順序封鎖法
順序封鎖法是預(yù)先對所存可加鎖的數(shù)據(jù)對象規(guī)定一個封鎖順序,每個事務(wù)都必須按照這個順序?qū)嵭蟹怄i,在釋放時,按照相反的順序進行。例如在B樹結(jié)構(gòu)的索引當中,可以規(guī)定加鎖的順序是從根節(jié)點開始到下一個子節(jié)點,依次內(nèi)推,
T1按照樹形結(jié)構(gòu)先對A加鎖,在對B加鎖,此時T2訪問不了A(要訪問B必須要先進過A),T1釋放A之后,T2才可以訪問,
****順序封鎖法可以有效的避免死鎖,但是實現(xiàn)起來十分困難,因為很難事先確定哪一個事物要訪問哪些數(shù)據(jù),因此很難按照順序去加鎖**
由此可見,數(shù)據(jù)庫當中不適合預(yù)防死鎖,只適合診斷和解除死鎖
7.死鎖的診斷與解除
數(shù)據(jù)庫系統(tǒng)當中診斷死鎖的方法和操作系統(tǒng)當中類似,一般使用超時法,事物等待圖法。
1.超時法:
如果一個事物的等待時間超過了規(guī)定的時限,那么就認為其發(fā)送了死鎖,超時法的劣勢十分明顯。
1.有可能誤判死鎖,事物可能由于其他的原因等待時間過長,超過了時限
2.若時限設(shè)置太長,則不能及時的發(fā)現(xiàn)死鎖。
2.事物等待圖:
事務(wù)等待圖是一個有向圖G=(T,U)。 T為結(jié)點的集合,每個結(jié)點表示正運行的事務(wù);U為邊的集合,每條邊表示事務(wù)等待的情況。若T1等待T2,則T1、T2之間劃一條有向邊,從T1指向T2。事務(wù)等待圖動態(tài)地反映了所有事務(wù)的等待情況。
并發(fā)控制子系統(tǒng)周期性地(比如每隔1分鐘)檢測事務(wù)等待圖,如果發(fā)現(xiàn)圖中存在回路,則表示系統(tǒng)中出現(xiàn)了死鎖。
死鎖的解決
數(shù)據(jù)庫管理系統(tǒng)當中的并發(fā)子系統(tǒng)一旦檢測到數(shù)據(jù)庫當中出現(xiàn)了死鎖,就要設(shè)法解除死鎖,通常的處理的方式是選擇一個處理死鎖代價最小的事物,將其撤銷,釋放次事物持有的所有鎖,使其它事物可以繼續(xù)運行下去,當然對撤銷的事物的所有操作要進行恢復(fù)。
二.事務(wù)隔離級別(惡果:臟讀 幻讀 不可重復(fù)讀)
1.事物的四大特性

2.事物隔離級別以及各級別下的并發(fā)訪問問題
Read uncommited(讀未提交):該隔離級別下可以讀出另一Session未提交的數(shù)據(jù),即臟讀
Read commited(讀已提交):可以避免臟讀,Session1多次讀取統(tǒng)一數(shù)據(jù),二次讀到的數(shù)據(jù)不一樣(另一個session對數(shù)據(jù)進行了修改),即不可重復(fù)讀(側(cè)重于對同一數(shù)據(jù)的修改),
Repeatable read(重復(fù)讀):次隔離級別可以避免不可重復(fù)讀,但是session1在對數(shù)據(jù)進行更新時,session2對表進行插入操作,session1發(fā)現(xiàn)更新的數(shù)據(jù)比原來的多,此為幻讀(側(cè)重于新增或者刪除)
Serializable:在此隔離級別下,所有的SQL執(zhí)行都會加上鎖,

3.快照讀和當前讀


當前讀:即是加了鎖的增刪改查語句,無論是排他鎖還是共享鎖都是當前讀,它讀取的是記錄的最新版本并且讀取之后還需要保證其他并發(fā)事物不能修改當前事物,故讀取的事物加鎖,其中除了select ...lock in share mode加了共享鎖之外,其他的幾個操作都會加排他鎖。
快照讀:簡單的讀操作,不加鎖,有可能會讀取到數(shù)據(jù)的老版本,當前讀是特殊的快照讀。
三.InnoDB在repeatable read隔離級別下如何避免幻讀

表象:是基于偽MVCC機制實現(xiàn)的快照讀(非阻塞讀),避免我們看到幻行.
在序列化狀態(tài)下真防止使幻讀的原因使因為加了next-key鎖(行鎖+gap鎖)
1.next-key鎖
next-key鎖:它由行鎖和gap鎖組成
行鎖:加載單個行記錄上的鎖
gap鎖:間隙鎖是鎖定一個范圍,但是不包括記錄本身(比如:id=1,id=3,鎖定(1,3]之間),gap鎖是為了方式幻讀而產(chǎn)生的,現(xiàn)在四大隔離級別當中,只有序列化和repeated read支持gap鎖,其他的2種隔離級別是不支持gap鎖的,gap鎖主要是防止插入的
2.repeated read 在什么情況下會使用gap鎖?
當我們使用范圍檢索而不是等值檢索數(shù)據(jù),并請求讀鎖和寫鎖的時候,InnoDB會給符合條件的數(shù)據(jù)加上鎖,對于在鍵值范圍內(nèi),但是并不存在的數(shù)據(jù),InnoDB也會加上鎖,這個鎖就是gap鎖,
舉例來說,假如 emp 表中只有 101 條記錄,其 empid 的值分別是 1,2,...,100,101,下 面的 SQL:
Select * from emp where empid > 100 for update;
是一個范圍條件的檢索,InnoDB 不僅會對符合條件的 empid 值為 101 的記錄加鎖,也會對 empid 大于 101(這些記錄并不存在)的“間隙”加鎖。