聲明:本欄目所使用的素材都是凱哥學(xué)堂VIP學(xué)員所寫宰掉,學(xué)員有權(quán)匿名呵哨,對文章有最終解釋權(quán)叽讳;凱哥學(xué)堂旨在促進VIP學(xué)員互相學(xué)習(xí)的基礎(chǔ)上公開筆記横殴。
之前我們介紹了行級鎖腌闯,顧名思義行級鎖就只是鎖住一行或多行數(shù)據(jù)芭概,因為針對的是行去鎖的硅堆,因為一個表格內(nèi)會有很多行數(shù)據(jù)校坑,要在這些數(shù)據(jù)中去鎖定其中幾行數(shù)據(jù)纯丸,是比較耗費資源剃氧。而表級鎖則是可以鎖住整個表玉组,所以相對于行級來說沒那么耗費資源谎柄,表級鎖有兩個模式:只讀模式和只寫模式,這和文件權(quán)限里的只讀只寫有點類似球切。 在一般情況下表格鎖并不經(jīng)常使用谷誓,在這里只是介紹一下如何使用表級鎖,和解鎖表級鎖吨凑,而且表級鎖的資料都可以在網(wǎng)絡(luò)上查找到捍歪,所以了解一下即可,在mysql官方也有表格鎖語法的文檔:
https://dev.mysql.com/doc/refman/5.6/en/lock-tables.html
LOCK TABLES 表名 READ
示例:
我們再打開一個客戶端來看看能否使用SELECT語句查詢這個帶有表級鎖的表格的數(shù)據(jù):
因為我們使用的是只讀模式的表級鎖糙臼,自然每個用戶都可以讀取、查詢這個表格的數(shù)據(jù)恩商,那么我們可以嘗試一下update這會對表格數(shù)據(jù)修改的語句能否執(zhí)行:
在行級鎖里即便某些行數(shù)據(jù)被上鎖了也還是能夠使用insert語句插入數(shù)據(jù)的变逃,那么我們試一下在表格鎖里是否能行得通:
從結(jié)果可以得知在表級鎖的只讀模式下,是不允許任何用戶對上鎖的表格進行任何的修改的怠堪。
自然的delete語句也無法使用:
那么如何解鎖呢揽乱?看看解鎖時會發(fā)生什么名眉,解鎖表級鎖的語法很簡單:
UNLOCK TABLES
示例:
使用只寫模式的表級鎖,語法:
LOCK TABLES 表名 WRITE
示例:
在表級鎖的只寫模式里凰棉,只有上鎖用戶可以對表格進行寫入數(shù)據(jù)损拢,其他用戶是不可以寫入數(shù)據(jù)的,其他用戶就連使用SELECT語句查詢數(shù)據(jù)都不可以:
上鎖用戶可以使用insert語句插入數(shù)據(jù)撒犀,其他用戶則不允許這個操作:
update語句也是一樣的:
還有delete語句:
如果用戶給一張表格上了表級鎖福压,那么這個用戶在給這個表格解鎖之前就只能操作這個表格,數(shù)據(jù)庫里的其他表格均不可以進行任何的操作或舞,如果操作就會報錯:
只寫模式的解鎖語法是一樣的荆姆,都是UNLOCK TABLES:
總結(jié)一下表級鎖,表級鎖就是針對表格進行鎖定映凳,相對于行級鎖沒那么耗資源胆筒,表級鎖有兩個模式,只讀模式和只寫模式诈豌,只讀模式下上鎖用戶和其他用戶都只能查詢數(shù)據(jù)不能寫入數(shù)據(jù)腐泻,只寫模式下上鎖用戶可以進行查詢數(shù)據(jù)和寫入數(shù)據(jù),其他用戶既不能查詢數(shù)據(jù)队询,也不能寫入數(shù)據(jù),執(zhí)行任何SQL語句都會進入等待狀態(tài)构诚,一直等到表格解鎖為止蚌斩,當表格解鎖的時候在等待中的事務(wù)會馬上被執(zhí)行。某個用戶對某個表格使用了表級鎖的話范嘱,就只能操作那個表格送膳,數(shù)據(jù)庫里的其他表格均不可進行任何操作。
悲觀鎖(Pessimistic Lock)是一種概念丑蛤、解決某些問題的模式叠聋,并不是一種特定的機制,悲觀鎖受裹,正如其名碌补,它指的是對數(shù)據(jù)被外界(包括本系統(tǒng)當前的其他事務(wù),以及來自外部系統(tǒng)的事務(wù)處理)修改持保守態(tài)度(悲觀)棉饶,因此厦章,在整個數(shù)據(jù)處理過程中,將數(shù)據(jù)處于鎖定狀態(tài)照藻。悲觀鎖的實現(xiàn)袜啃,往往依靠數(shù)據(jù)庫提供的鎖機制(也只有數(shù)據(jù)庫層提供的鎖機制才能真正保證數(shù)據(jù)訪問的排他性,否則幸缕,即使在本系統(tǒng)中實現(xiàn)了加鎖機制群发,也無法保證外部系統(tǒng)不會修改數(shù)據(jù))晰韵。
所以簡單來說悲觀鎖就是很悲觀,每次去拿數(shù)據(jù)的時候都認為別人會修改熟妓,所以每次在拿數(shù)據(jù)的時候都會上鎖雪猪,這樣別人想拿這個數(shù)據(jù)就會block直到它拿到鎖。傳統(tǒng)的關(guān)系型數(shù)據(jù)庫里邊就用到了很多這種鎖機制滑蚯,比如行鎖浪蹂,表鎖等,讀鎖告材,寫鎖等坤次,都是在做操作之前先上鎖。
例如之前我們做的火車票務(wù)系統(tǒng)的小案例斥赋,就是使用的悲觀鎖的方式缰猴,在我們的代碼里都是借助于數(shù)據(jù)庫自帶的鎖機制完成的,當用戶A在購票時用戶B就不能夠購票疤剑,或者購票失敗滑绒,這就是在整個數(shù)據(jù)處理過程中,將數(shù)據(jù)處于鎖定狀態(tài)隘膘,具有很明顯的排他性(悲觀)疑故。 代碼:
悲觀并發(fā)控制實際上是“先取鎖再訪問”的保守策略,為數(shù)據(jù)處理的安全提供了保證弯菊。但是在效率方面纵势,處理加鎖的機制會讓數(shù)據(jù)庫產(chǎn)生額外的開銷,還有增加產(chǎn)生死鎖的機會管钳;另外钦铁,在只讀型事務(wù)處理中由于不會產(chǎn)生沖突,也沒必要使用鎖才漆,這樣做只能增加系統(tǒng)負載牛曹;還有會降低了并行性,一個事務(wù)如果鎖定了某行數(shù)據(jù)醇滥,其他事務(wù)就必須等待該事務(wù)處理完才可以處理那數(shù)據(jù)黎比。
樂觀鎖( Optimistic Locking ) 相對悲觀鎖而言,樂觀鎖假設(shè)認為數(shù)據(jù)一般情況下不會造成沖突鸳玩,所以在數(shù)據(jù)進行提交更新的時候焰手,才會正式對數(shù)據(jù)的沖突與否進行檢測,如果發(fā)現(xiàn)沖突了怀喉,則讓返回用戶錯誤的信息书妻,讓用戶決定如何去做。 相對于悲觀鎖,在對數(shù)據(jù)庫進行處理的時候躲履,樂觀鎖并不會使用數(shù)據(jù)庫提供的鎖機制见间。一般的實現(xiàn)樂觀鎖的方式就是記錄數(shù)據(jù)版本。
數(shù)據(jù)版本,為數(shù)據(jù)增加的一個版本標識工猜。當讀取數(shù)據(jù)時米诉,將版本標識的值一同讀出,數(shù)據(jù)每更新一次篷帅,同時對版本標識進行更新史侣。當我們提交更新的時候,判斷數(shù)據(jù)庫表對應(yīng)記錄的當前版本信息與第一次取出來的版本標識進行比對魏身,如果數(shù)據(jù)庫表當前版本號與第一次取出來的版本標識值相等惊橱,則予以更新,否則認為是過期數(shù)據(jù)箭昵。
所以實際上樂觀鎖和悲觀鎖一樣也是一種概念税朴、解決某些業(yè)務(wù)需求的模式,并不是一種特定的機制家制,樂觀鎖的主要實現(xiàn)方式是我們開發(fā)人員自己通過在數(shù)據(jù)庫中增加一條存儲數(shù)據(jù)版本的列正林,然后通過代碼來判斷這些數(shù)據(jù)的版本,而不是借助數(shù)據(jù)庫自帶的鎖來完成的一種鎖定數(shù)據(jù)的模式颤殴。
例如我們假設(shè)一種情況:用戶A和用戶B同時在ATM機里往一個賬戶余額2000元的賬戶取款觅廓,兩個用戶都查詢到賬戶還有2000元,所以用戶A要取款1500涵但,用戶B要取款1000哪亿,然后這兩個取款事務(wù)會同時提交,如果這個銀行的系統(tǒng)邏輯寫得不夠好的話贤笆,就會出現(xiàn)2000-1500-1000=-500的結(jié)果,賬戶余額就會出現(xiàn)負數(shù)讨阻。
示意圖:
在這種取款的情況下芥永,如果使用悲觀鎖來鎖住數(shù)據(jù)的話,由于其排他性钝吮,那么另外一個用戶就無法查詢賬戶余額埋涧,只能處于等待狀態(tài),因為在悲觀鎖里在事務(wù)結(jié)束之前數(shù)據(jù)都是處于鎖定狀態(tài)奇瘦,而且在銀行在這種數(shù)據(jù)量大的地方棘催,使用共享鎖這種行級鎖也耗費資源。所以就需要用到樂觀鎖了耳标,樂觀鎖只有在操作提交的時候才會去鎖定數(shù)據(jù)醇坝。在樂觀鎖中我們可以給數(shù)據(jù)設(shè)定一個版本號,一旦這個數(shù)據(jù)發(fā)生修改次坡,版本號就會發(fā)生變化呼猪,每一個操作都會先判斷版本號是否是最新的版本號画畅,不是的話就不允許操作,在樂觀鎖的實現(xiàn)過程中我們并不會使用到數(shù)據(jù)庫自帶的鎖宋距,所以用戶們都可以任意的查詢或提交操作轴踱。
示意圖:
下面我們做一個簡單的取款系統(tǒng)來演示如何實現(xiàn)樂觀鎖:
先準備一個表格里面填充一行數(shù)據(jù):
代碼示例:
運行結(jié)果:
樂觀并發(fā)控制相信事務(wù)之間的數(shù)據(jù)競爭(data race)的概率是比較小的,因此盡可能直接做下去谚赎,直到提交的時候才去鎖定淫僻,所以不會產(chǎn)生任何鎖和死鎖。但如果直接簡單這么做壶唤,還是有可能會遇到不可預(yù)期的結(jié)果雳灵,例如兩個事務(wù)都讀取了數(shù)據(jù)庫的某一行,經(jīng)過修改以后寫回數(shù)據(jù)庫视粮,這時就遇到了問題细办。
臟讀就是指當一個事務(wù)正在訪問數(shù)據(jù),并且對數(shù)據(jù)進行了修改蕾殴,而這種修改還沒有提交到數(shù)據(jù)庫中笑撞,這時,另外一個事務(wù)也訪問這個數(shù)據(jù)钓觉,然后使用了這個數(shù)據(jù)茴肥。因為這個【學(xué)Java,到凱哥學(xué)堂kaige123.com】數(shù)據(jù)是還沒有提交的數(shù)據(jù)荡灾,那么另外一個事務(wù)讀到的這個數(shù)據(jù)是臟數(shù)據(jù)(Dirty Data)瓤狐,依據(jù)臟數(shù)據(jù)所做的操作可能是不正確的。
在一個事務(wù)內(nèi)批幌,多次讀同一個數(shù)據(jù)础锐。在這個事務(wù)還沒有結(jié)束時,另一個事務(wù)也訪問該同一數(shù)據(jù)荧缘。那么皆警,在第一個事務(wù)的兩次讀數(shù)據(jù)之間。由于第二個事務(wù)的修改截粗,那么第一個事務(wù)讀到的數(shù)據(jù)可能不一樣信姓,這樣就發(fā)生了在一個事務(wù)內(nèi)兩次讀到的數(shù)據(jù)是不一樣的,因此稱為不可重復(fù)讀绸罗,即原始讀取不可重復(fù)意推。
幻讀是指當事務(wù)不是獨立執(zhí)行時發(fā)生的一種現(xiàn)象,例如第一個事務(wù)對一個表中的數(shù)據(jù)進行了修改珊蟀,比如這種修改涉及到表中的“全部數(shù)據(jù)行”菊值。同時,第二個事務(wù)也修改這個表中的數(shù)據(jù),這種修改是向表中插入“一行新數(shù)據(jù)”俊性。那么略步,以后就會發(fā)生操作第一個事務(wù)的用戶發(fā)現(xiàn)表中還存在沒有修改的數(shù)據(jù)行,就好象發(fā)生了幻覺一樣.一般解決幻讀的方法是增加范圍鎖RangeS定页,鎖定檢索范圍為只讀趟薄,這樣就避免了幻讀。