背景mysql
考慮下面兩個併發帶來的問題:sql
一、丟失更新:一個事務的更新結果覆蓋了其它事務的更新結果,即所謂的更新丟失。數據庫
二、髒讀:當一個事務讀取其它完成一半事務的記錄時,就會發生髒讀取。bash
例如:併發
兩個用戶同時修改商品庫存表,A、B同時進入,看到的庫存都是100,A購買一件把庫存修改成99(100-1)。此時B購買兩件把庫存修改成98(100-2),由於A、B同時讀到的庫存都是100,B並不能看到A作的庫存更新,因此形成B髒讀,形成A丟失更新。性能
因此爲了解決這些併發帶來的問題。 咱們須要引入併發控制機制--鎖。ui
鎖分類spa
悲觀鎖3d
悲觀鎖就是用戶修改數據時看起來很悲觀,保守態度,擔憂別的用戶會同時修改這條數據,因此每次修改時會提早把這條數據鎖定起來,只有本身可修改(但別的用戶能夠讀),等本身修改完了再釋放鎖。code
樂觀鎖
樂觀鎖就是用戶修改數據時心態很樂觀,無論別人修改不修改數據,我都不上鎖,我修改的時候判斷下數據有沒有發生變化,沒發生變化我就會更新成功,發生變化了就不會更新成功我再去重試以前的動做直到更新成功。
鎖應用
悲觀鎖
使用悲觀鎖的時候咱們首先必須關閉mysql數據庫的自動提交屬性,由於MySQL默認使用autocommit模式,也就是說,當你執行一個更新操做後,MySQL會馬上將結果進行提交。
關閉命令爲:set autocommit=0;
悲觀鎖通常使用select…for update實現,在執行的時候會鎖定數據,雖然會鎖定數據,可是不影響其餘事務的普通查詢使用。
在咱們使用悲觀鎖的時候事務中的語句例如:
//開始事務
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 同一條數據時會等待其它事務結束後才執行,通常的SELECT查詢則不受影響。
注意事項
執行事務時關鍵字select…for update會鎖定數據,防止其餘事務更改數據。可是鎖定數據也是有規則的。
查詢條件與鎖定範圍:
好比查詢條件爲主鍵ID=1等等,若是此條數據存在,則鎖定當前行數據,若是不存在,則不鎖定。
好比查詢條件爲主鍵ID>1等等,此時會鎖定整張數據表。
會鎖定整張數據表。
明確指定索引而且查到,則鎖定整條數據。若是找不到指定索引數據,則不加鎖。
樂觀鎖
例如表
student(id,name,version)
1 a 1
複製代碼
當事務一進行更新操做:
update student set name='txt' where id = #{id} and version = #{version};
複製代碼
此時操做完後數據會變爲id = 1,name = txt,version = 2,當另一個事務二一樣執行更新操做的時候,卻發現version != 1,此時事務二就會操做失敗,從而保證了數據的正確性。
二、使用時間戳來實現,原理同上。
三、使用其餘數據庫字段,如:金額,更新時添加條件判斷金額是否變化,原理同上。
樂觀鎖圖示
結論
兩種鎖各有優缺點,不能單純的定義哪一個好於哪一個。樂觀鎖比較適合數據修改比較少,讀取比較頻繁的場景,即便出現了少許的衝突,這樣也省去了大量的鎖的開銷,故而提升了系統的吞吐量。可是若是常常發生衝突(寫數據比較多的狀況下),上層應用不不斷的retry,這樣反而下降了性能,對於這種狀況使用悲觀鎖就更合適。