關于mysql中的樂觀鎖和悲觀鎖面試的時候被問到的概率還是比較大的。
成都創(chuàng)新互聯(lián)服務項目包括官渡網(wǎng)站建設、官渡網(wǎng)站制作、官渡網(wǎng)頁制作以及官渡網(wǎng)絡營銷策劃等。多年來,我們專注于互聯(lián)網(wǎng)行業(yè),利用自身積累的技術優(yōu)勢、行業(yè)經驗、深度合作伙伴關系等,向廣大中小型企業(yè)、政府機構等提供互聯(lián)網(wǎng)行業(yè)的解決方案,官渡網(wǎng)站推廣取得了明顯的社會效益與經濟效益。目前,我們服務的客戶以成都為中心已經輻射到官渡省份的部分城市,未來相信會繼續(xù)擴大服務區(qū)域并繼續(xù)獲得客戶的支持與信任!
mysql的悲觀鎖:
其實理解起來非常簡單,當數(shù)據(jù)被外界修改持保守態(tài)度,包括自身系統(tǒng)當前的其他事務,以及來自外部系統(tǒng)的事務處理,因此,在整個數(shù)據(jù)處理過程中,將數(shù)據(jù)處于鎖定狀態(tài)。悲觀鎖的實現(xiàn),往往依靠數(shù)據(jù)庫提供的鎖機制,但是也只有數(shù)據(jù)庫層提供的鎖機制才能真正保證數(shù)據(jù)訪問的排他性,否則,即使在自身系統(tǒng)中實現(xiàn)了加鎖機制,也無法保證外部系統(tǒng)不會修改數(shù)據(jù)。
來點實際的,當我們使用悲觀鎖的時候我們首先必須關閉mysql數(shù)據(jù)庫的自動提交屬性,因為MySQL默認使用autocommit模式,也就是說,當你執(zhí)行一個更新操作后,MySQL會立刻將結果進行提交。
關閉命令為:set autocommit=0;
悲觀鎖可以使用select…for update實現(xiàn),在執(zhí)行的時候會鎖定數(shù)據(jù),雖然會鎖定數(shù)據(jù),但是不影響其他事務的普通查詢使用。此處說普通查詢就是平時我們用的:select * from table 語句。在我們使用悲觀鎖的時候事務中的語句例如:
//開始事務
begin;/begin work;/start transaction; (三選一)
//查詢信息
select * from order where id=1 for update;
//修改信息
update order set name='names';
//提交事務
commit;/commit work;(二選一)
此處的查詢語句for update關鍵字,在事務中只有SELECT ... FOR UPDATE 或LOCK IN SHARE MODE 同一條數(shù)據(jù)時會等待其它事務結束后才執(zhí)行,一般的SELECT查詢則不受影響。
執(zhí)行事務時關鍵字select…for update會鎖定數(shù)據(jù),防止其他事務更改數(shù)據(jù)。但是鎖定數(shù)據(jù)也是有規(guī)則的。
查詢條件與鎖定范圍:
1、具體的主鍵值為查詢條件
比如查詢條件為主鍵ID=1等等,如果此條數(shù)據(jù)存在,則鎖定當前行數(shù)據(jù),如果不存在,則不鎖定。
2、不具體的主鍵值為查詢條件
比如查詢條件為主鍵ID1等等,此時會鎖定整張數(shù)據(jù)表。
3、查詢條件中無主鍵
會鎖定整張數(shù)據(jù)表。
4、如果查詢條件中使用了索引為查詢條件
明確指定索引并且查到,則鎖定整條數(shù)據(jù)。如果找不到指定索引數(shù)據(jù),則不加鎖。
悲觀鎖的確保了數(shù)據(jù)的安全性,在數(shù)據(jù)被操作的時候鎖定數(shù)據(jù)不被訪問,但是這樣會帶來很大的性能問題。因此悲觀鎖在實際開發(fā)中使用是相對比較少的。
mysql的樂觀鎖:
相對悲觀鎖而言,樂觀鎖假設數(shù)據(jù)一般情況下不會造成沖突,所以在數(shù)據(jù)進行提交更新的時候,才會對數(shù)據(jù)的沖突與否進行檢測,如果發(fā)現(xiàn)沖突,則讓返回用戶錯誤的信息,讓用戶決定如何去做。
一般來說,實現(xiàn)樂觀鎖的方法是在數(shù)據(jù)表中增加一個version字段,每當數(shù)據(jù)更新的時候這個字段執(zhí)行加1操作。這樣當數(shù)據(jù)更改的時候,另外一個事務訪問此條數(shù)據(jù)進行更改的話就會操作失敗,從而避免了并發(fā)操作錯誤。當然,還可以將version字段改為時間戳,不過原理都是一樣的。
例如有表student,字段:
id,name,version
1 a 1
當事務一進行更新操作:update student set name='ygz' where id = #{id} and version = #{version};
此時操作完后數(shù)據(jù)會變?yōu)閕d = 1,name = ygz,version = 2,當另外一個事務二同樣執(zhí)行更新操作的時候,卻發(fā)現(xiàn)version != 1,此時事務二就會操作失敗,從而保證了數(shù)據(jù)的正確性。
悲觀鎖和樂觀鎖都是要根據(jù)具體業(yè)務來選擇使用,本文僅作簡單介紹。
你說的這個version是mysql底層的鎖機制提供的,并不是java提供的。
使用數(shù)據(jù)版本(Version)記錄機制實現(xiàn),這是mysql樂觀鎖最常用的一種實現(xiàn)方式。所謂的數(shù)據(jù)版本就是給數(shù)據(jù)增加一個版本標識,一般是通過為數(shù)據(jù)庫表增加一個數(shù)字類型的 “version” 字段來實現(xiàn)。當讀取數(shù)據(jù)時,將version字段的值一同讀出,數(shù)據(jù)每更新一次,對此version值加1。當我們提交更新的時候,判斷數(shù)據(jù)庫表對應記錄的當前版本信息與第一次取出來的version值進行比對,如果數(shù)據(jù)庫表當前版本號與第一次取出來的version值相等,則予以更新,否則認為是過期數(shù)據(jù),版本號重新讀取再做更新。
以上不是重點,重點是 對事務控制語句不熟悉。
SAVEPOINT identifier : 在事務中 創(chuàng)建保存點。一個事務中 允許有多個保存點。
RELEASE SAVEPOINT identifier : 刪除保存點。當事務中 沒有指定的 保存點,執(zhí)行該語句 會拋異常。
ROLLBACK TO identifier : 把事務回滾到 保存點。
Say you have a table T with a column C with one row in it, say it has the value '1'. And consider you have a simple task like following:
That is a simple task that issue two reads from table T, with a delay of 1 minute between them.
If you follow the logic above you can quickly realize that SERIALIZABLE transactions, while they may make life easy for you, are always completely blocking every possible concurrent operation, since they require that nobody can modify, delete nor insert any row. The default transaction isolation level of the .Net System.Transactions scope is serializable, and this usually explains the abysmal performance that results.
在Repeatable Read隔離級別下,一個事務可能會遇到幻讀(Phantom Read)的問題。
幻讀是指,在一個事務中,第一次查詢某條記錄,發(fā)現(xiàn)沒有,但是,當試圖更新這條不存在的記錄時,竟然能成功,并且,再次讀取同一條記錄,它就神奇地出現(xiàn)了。
我們仍然先準備好students表的數(shù)據(jù):
然后,分別開啟兩個MySQL客戶端連接,按順序依次執(zhí)行事務A和事務B:
事務B在第3步第一次讀取id=99的記錄時,讀到的記錄為空,說明不存在id=99的記錄。隨后,事務A在第4步插入了一條id=99的記錄并提交。事務B在第6步再次讀取id=99的記錄時,讀到的記錄仍然為空,但是,事務B在第7步試圖更新這條不存在的記錄時,竟然成功了,并且,事務B在第8步再次讀取id=99的記錄時,記錄出現(xiàn)了。
可見,幻讀就是沒有讀到的記錄,以為不存在,但其實是可以更新成功的,并且,更新成功后,再次讀取,就出現(xiàn)了。
在沖突較少的情況下,使用樂觀鎖。樂觀鎖 因為沒有 加鎖 釋放鎖,也減少了 加鎖 釋放鎖的開銷。
沖突較多時,如果使用樂觀鎖 需要不停地嘗試,所以 使用悲觀鎖。
如果樂觀鎖 進行嘗試時 的花銷較大,也是使用悲觀鎖。