參考網址: https://zhuanlan.zhihu.com/p/31537871
1、什么悲觀鎖?
顧名思義,悲觀鎖是基於一種悲觀的態度類來防止一切數據沖突,它是以一種預防的姿態在修改數據之前把數據鎖住,然后再對數據進行讀寫,在它釋放鎖之前任何人都不能對其數據進行操作,直到前面一個人把鎖釋放后下一個人數據加鎖才可對數據進行加鎖,然后才可以對數據進行操作,一般數據庫本身鎖的機制都是基於悲觀鎖的機制實現的;
特點:可以完全保證數據的獨占性和正確性,因為每次請求都會先對數據進行加鎖, 然后進行數據操作,最后再解鎖,而加鎖釋放鎖的過程會造成消耗,所以性能不高;
手動加悲觀鎖:讀鎖LOCK tables test_db read釋放鎖UNLOCK TABLES;
寫鎖:LOCK tables test_db WRITE釋放鎖UNLOCK TABLES;

3、什么是樂觀鎖?
樂觀鎖是對於數據沖突保持一種樂觀態度,操作數據時不會對操作的數據進行加鎖(這使得多個任務可以並行的對數據進行操作),只有到數據提交的時候才通過一種機制來驗證數據是否存在沖突(一般實現方式是通過加版本號然后進行版本號的對比方式實現);
特點:樂觀鎖是一種並發類型的鎖,其本身不對數據進行加鎖通而是通過業務實現鎖的功能,不對數據進行加鎖就意味着允許多個請求同時訪問數據,同時也省掉了對數據加鎖和解鎖的過程,這種方式因為節省了悲觀鎖加鎖的操作,所以可以一定程度的的提高操作的性能,不過在並發非常高的情況下,會導致大量的請求沖突,沖突導致大部分操作無功而返而浪費資源,所以在高並發的場景下,樂觀鎖的性能卻反而不如悲觀鎖。
樂觀鎖實現形式:
表A 字段

現在有兩個請求同時操作表A
操作1先查詢出需要檢索的數據為:

操作2也查詢出需要檢索的數據為:

操作1把Name字段數據修改成lisi 比操作2先提交;
此時提交操作1會把之前查詢到的version與現在的數據的version進行比較,版本相同則可以提交,版本不同則視為數據過期:
SQL語句:update A set Name=lisi,version=version+1 where ID=#{id} and version=#{version}
此時數據庫變成了如下

那么最后操作2修改了數據來提交的時候是以操作1同樣的方式,當然最后肯定不能提交成功,因為他手里的版本號和現在數據庫里的版本號不匹配;
最后盜圖一張來解釋

參考網址:https://www.cnblogs.com/frankyou/p/4667110.html
--------------------------------------------------------
什么是悲觀鎖和樂觀鎖?
悲觀鎖(Pessimistic Lock), 顧名思義,就是很悲觀,每次去拿數據的時候都認為別人會修改,所以每次在拿數據的時候都會上鎖,這樣別人想拿這個數據就會block直到它拿到鎖。傳統的關系型數據庫里邊就用到了很多這種鎖機制,比如行鎖,表鎖等,讀鎖,寫鎖等,都是在做操作之前先上鎖。
樂觀鎖(Optimistic Lock), 顧名思義,就是很樂觀,每次去拿數據的時候都認為別人不會修改,所以不會上鎖,但是在更新的時候會判斷一下在此期間別人有沒有去更新這個數據,可以使用版本號等機制。樂觀鎖適用於多讀的應用類型,這樣可以提高吞吐量,像數據庫如果提供類似於write_condition機制的其實都是提供的樂觀鎖。
悲觀鎖
格式 SELECT…FOR UPDATE
例:select * from account where name="Erica" for update
這條 sql 語句鎖定了 account 表中所有符合檢索條件( name=“Erica” )的記錄。 本次事務提交之前(事務提交時會釋放事務過程中的鎖),外界無法修改這些記錄。
格式 SELECT…FOR UPDATE NOWAIT
該關鍵字的含義是“不用等待,立即返回”,如果當前請求的資源被其他會話鎖定時,會發生阻塞,nowait可以避免這一阻塞。
MySQL中用悲觀鎖務必須確定走了索引,而不是全表掃描,否則將會將整個數據表鎖住。
現在互聯網高並發的架構中,受到fail-fast思路的影響,悲觀鎖已經非常少見了。
樂觀鎖
樂觀鎖,大多是基於數據版本 ( Version )記錄機制實現。何謂數據版本?即為數據增加一個版本標識,在基於數據庫表的版本解決方案中,一般是通過為數據庫表增加一個 “version” 字段來實現。
讀取出數據時,將此版本號一同讀出,之后更新時,對此版本號加一。此時,將提交數據的版本數據與數據庫表對應記錄的當前版本信息進行比對,如果提交的數據版本號大於數據庫表當前版本號,則予以更新,否則認為是過期數據。
要使用悲觀鎖,我們必須關閉mysql數據庫的自動提交屬性,因為MySQL默認使用autocommit模式,也就是說,當你執行一個更新操作后,MySQL會立刻將結果進行提交。set autocommit=0;
使用START TRANSACTION,自動提交將保持禁用狀態,直到你使用COMMIT或ROLLBACK結束事務。 自動提交模式然后恢復到之前的狀態(如果start transaction 前 autocommit = 1,則完成本次事務后 autocommit 還是 1。如果 start transaction 前 autocommit = 0,則完成本次事務后 autocommit 還是 0)
通過減小鎖的粒度,可以提高系統的吞吐量;如以下實現邏輯,只要庫存>1,事務就可以成功更新庫存數量;如果采用傳統的 version 在高並發的情況下可能會導致大量的並發事務更新失敗。
// 仍挑選以庫存數作為樂觀鎖 //step1: 查詢出商品信息 select (inventory) from items where id=100; //step2: 根據商品信息生成訂單 insert into orders(id,item_id) values(null,100); //step3: 修改商品的庫存 update items set inventory=inventory-1 where id=100 and inventory > 1;
總結:
兩種鎖各有優缺點,不可認為一種好於另一種,像樂觀鎖適用於寫比較少的情況下,即沖突真的很少發生的時候,這樣可以省去了鎖的開銷,加大了系統的整個吞吐量。但如果經常產生沖突,上層應用會不斷的進行retry,這樣反倒是降低了性能,所以這種情況下用悲觀鎖就比較合適。

