php - 高並發下悲觀鎖與樂觀鎖的選擇問題
ringa_lee
ringa_lee 2017-05-16 13:05:01
0
4
857

說說我的看法,不對的地方請指正。

樂觀鎖的實作原理是cas操作,java中輕量級鎖也是基於cas實現的。

悲觀鎖最大的問題就是阻塞問題。

在深入理解java虛擬機中提到,輕量級鎖一般情況下是優於重量級鎖(互斥鎖)的;如果在高並發鎖競爭比較激烈的情況下輕量級鎖會由於長時間自旋消耗cpu 從而使得輕量級鎖的性能比傳統的重量級鎖更慢。那麼樂觀鎖中也有自旋和cas,所以高並發下樂觀鎖好像不是好的解決方案。

但是有些博文中提到 高並發下使用樂觀鎖更合適
比如這篇文章中就提到高並發數據庫訪問使用樂觀鎖
http://blog.csdn.net/amqvje/a...

問題1,高並發下如何選擇?

問題2 樂觀鎖有什麼缺點? 為什不都使用樂觀鎖。高並發下都是使用樂觀鎖,如果並發量不高,使用樂觀鎖感覺更不是問題

ringa_lee
ringa_lee

ringa_lee

全部回覆(4)
刘奇

簡單的來說,一般情況下,樂觀鎖適合只有讀沒有寫的操作,悲觀鎖適合於讀寫混合的操作。如果寫入操作非常簡單短小,例如增加訪問人數,也可以用樂觀鎖,或者不需要確保讀到的數據是最新的時候。 80%的情況下使用樂觀鎖確實是比較好的選擇。

CAS依賴硬體CPU指令支援去實現原子級操作,所以高並發的情況下一般會更快,但是這個快不是沒有缺點的,缺點就是有讀也有寫的時候,CPU緩存失效率可能會增加,除非你的CPU是單核心的(非Intel平台的嵌入式)。

總而言之,要提升高並發效能,還是要實際測量的數據說明問題,我上面提到的都是理論。希望對你有幫助。

阿神

無論是悲觀鎖還是樂觀鎖,其實都是並發控制的一種思想,並不僅限於資料庫,具體如何選擇樂觀鎖和悲觀鎖是根據業務場景來的。
悲觀鎖:
一般情況下,我們使用的悲觀鎖就是在資料庫層面增加一個排它鎖,加鎖成功就可以修改資料然後提交事務,事務提交成功解鎖,失敗就說明資料正在被修改。如果你使用mysql的innodb的話,要注意 set autocommit=0關閉mysql自動提交屬性,因為mysql預設使用autocommit模式,就是說,當你執行一個更新操作後,MySQL會立刻將結果進行提交,另外還要注意鎖的級別,預設innodb是使用行級鎖,但是行級鎖是基於索引的,如果你這條sql沒用索引,那麼mysql就會使用表級鎖鎖表了。
優缺點:
悲觀鎖是「先取鎖再存取」的保守策略,為資料處理的安全提供了保證。但是在效率方面,處理加鎖的機制會讓資料庫產生額外的開銷,還有增加產生死鎖的機會。另外,在唯讀型事務處理中由於不會產生衝突,也沒必要使用鎖,這樣做只能增加系統負載。還有會降低了並行性,一個事務如果鎖定了某行數據,其他事務就必須等待該事務處理完才可以處理那行數據。

樂觀鎖:
樂觀鎖其實就是假設認為數據一般情況下不會造成衝突,所以在數據進行提交更新的時候,才會正式對數據的衝突與否進行檢測,如果發現衝突了,則讓返回用戶錯誤的訊息,讓使用者決定如何做。
一般直接寫在程式碼邏輯層就可以了,相對於悲觀鎖,在對資料庫進行處理的時候,樂觀鎖並不會使用資料庫提供的鎖機制,通常採用一個version版本號或時間戳來實現。使用版本號時,可以在資料初始化時指定一個版本號,每次對資料的更新操作都會對版本號執行+1操作。並判斷目前版本號是不是該資料的最新的版本號。如:

1.查询出商品信息
select (status,version) from t_goods where id=#{id}
2.根据商品信息生成订单
3.修改商品status为2
update t_goods 
set status=2,version=version+1
where id=#{id} and version=#{version};

優缺點:
樂觀鎖認為資料競爭的機率是很小的,因此,盡可能直接做下去,直到提交的時候才去鎖定,所以不會產生任何鎖和死鎖。但如果直接簡單這麼做,還是有可能會遇到不可預期的結果,例如兩個事務都讀取了資料庫的某一行,經過修改以後寫回資料庫,這時就遇到了問題。
樂觀鎖存在失效的情況,屬小機率事件,需要多個條件共同配合才會出現。如:

  • 應用程式採用自己的策略管理主鍵ID。如,常見的取目前ID欄位的最大值+1作為新ID。

  • 版本號欄位 version 預設值為 0 。

  • 用戶A讀取了某個記錄準備修改它。該記錄正好是ID最大的記錄,且之前沒有被修改過,version 為預設值 0。

  • 在用戶A讀取完成後,用戶B恰好刪除了該記錄。之後,用戶C又插入了一個新記錄。

  • 此時,陰差陽錯的,新插入的記錄的ID與用戶A讀取的記錄的ID是一致的, 而版本號兩者又都是預設值 0。

  • 用戶A在用戶C操作完成後,修改完成記錄並儲存。由於ID、version均可以匹配上,因此用戶A成功保存。但是,卻把用戶C插入的記錄覆蓋掉了。
    樂觀鎖此時的失效,根本原因在於應用所使用的主鍵ID管理策略, 正好與樂觀鎖存在極小程度上的不相容。

PHPzhong

沒工作之前我也是和題主相同的想法,實際工作中,其實兩者差別不大,真正高並發下系統耗時的地方永遠是網絡連接,數據庫查詢以及線程的主動睡眠,鎖帶來開銷基本上可以忽略不計

我想大声告诉你

關鍵問題在於,事實是悲觀的還是樂觀的?

假如你的資源競爭很激烈,而且無法共享的話,樂觀鎖不過是讓大量請求的希望落空罷了。

假如你的資源沒什麼競爭(這個和並發高低沒必然的關聯,業務的影響更大),那悲觀鎖意味著不必要地加鎖。如果原本是可共享的資源(例如資源支援多個唯讀方),那麼悲觀鎖意味著失去原本的可以使用的時間。

在深入理解java虛擬機中提到,輕量級鎖一般情況下是優於重量級鎖(互斥鎖)的;如果在高並發鎖競爭比較激烈的情況下輕量級鎖會由於長時間自旋消耗cpu 從而使得輕量級鎖的性能比傳統的重量級鎖更慢。那麼樂觀鎖裡也有自旋和cas,所以高並發下悲觀鎖好像不是個好的解決方案。

我並不了解 JVM。不過「樂觀鎖中也有自旋和cas」是什麼意思? cas 就是自旋鎖的一種實現方式,為什麼要並列呢? 「也」是什麼意思?悲觀鎖是個阻塞操作,沒有自旋,也不會持續消耗 CPU 的。

熱門教學
更多>
最新下載
更多>
網站特效
網站源碼
網站素材
前端模板