高性能MySQL學習筆記(2) —— 併發控制

MySQL 併發控制

  前一節已經說過了,MySQL是多線程應用,並且共享存儲數據,很顯然,當兩個及以上線程對同一塊數據進行寫將會發生數據不一致等各種問題,比如,同時對一個表增加一條記錄,後一個增加的記錄可能會覆蓋前一條,造成數據丟失。若僅僅是讀不會發生錯誤,但是當讀寫一同,就有可能發生讀錯誤,所以,對讀也是需要必要的控制。
  關於數據讀寫錯誤的會有哪幾種情況,可以參考:事務隔離級別
  以上問題就需要併發控制來解決,所謂的併發,就是每一次只允許一個線程對某一塊數據(可以是某個數據庫,或某張表,或表裏某條記錄)寫,實現併發控制有多種方式,MySQL採用的是鎖以及MVCC(多版本控制)。
  (在我看來,同步和併發實質上是一個概念,併發只是更小更細粒度的同步。)
  MySQL高性能一書中提到:MySQL提供兩個層面的併發控制:服務器級和存儲引擎級別。但並沒有具體解釋這兩個是什麼意思,查到的資料也很少說明,雖然從字面上能大體懂得是什麼。
  服務器級:由MySQL(具體應該是SQL處理層)實現,對整個MySQL服務器(全局鎖,實際上通過表鎖實現)實現併發控制。
  存儲引擎級:由各個存儲引擎實現,可以是表鎖,也可以是行級鎖。
  (用戶也可以通過SQL命令進行手動加鎖)

讀寫鎖

  MySQL提供了兩種鎖實現併發控制:讀鎖和寫鎖。讀鎖是共享的,也叫共享鎖(也叫S鎖),相互不會阻塞,多個讀鎖(多個線程用戶)可以同一時刻讀取統一資源;寫鎖則是排他的,也叫排他鎖(也叫X鎖),同一時間一個資源只能有一個寫鎖,也就是說,寫鎖會阻塞其他寫鎖和讀鎖。即讀鎖上面可以加讀鎖,但不能加寫鎖,而寫鎖則不能加任何鎖。
  上面說的讀寫鎖的“讀寫”不是針對操作,而是針對數據,鎖的是數據,每次操作數據先判斷該數據是否加鎖,加了什麼鎖,然後以此判斷是否允許本次操作執行,但這樣是不是覺得很麻煩?很耗性能?所以纔有了數據庫事務隔離級別,統一設置一個隔離級別,數據庫系統會根據隔離級別隱式的給數據加鎖,然後根據這個級別來判斷本次操作執行權限,關於事務隔離級別詳見:事務隔離級別

樂觀悲觀鎖

  注意,不管是樂觀鎖,還是悲觀鎖,都是一種思想,而不是像讀鎖、寫鎖一樣,是一種具體實現方式。
  悲觀鎖的思想:操作前,悲觀地認爲所操作數據在操作期間會被其他事務修改,所以,在操作前我要先給我操作的數據加鎖才放心。至於加的是讀鎖還是寫鎖則看具體應用場景。
  樂觀鎖的思想:操作前,樂觀地認爲所操作數據在操作期間不會被其他事務修改,我不加鎖(別的事務操作期間就可對該數據操作),只在最後更新的時候(如果操作是更新的話)查看原始數據是否被修改,如果沒修改,更新數據,否則失敗。至於如何知道原始數據被修改,這就是涉及到具體實現方式了,最常用的就是MVCC(樂觀鎖可以通過MVCC實現,但不是說,MVCC是樂觀鎖的一種,MVCC也可以採用悲觀鎖實現,二者是平行關係),還有CAS。
  兩種思想並沒有優劣,而是要看具體應用,樂觀鎖適用於讀多寫少的應用,因爲減少了鎖的開銷;悲觀鎖適用於頻繁寫操作的應用(寫操作多的話,對於樂觀鎖會帶來頻繁的操作失敗,從而重新進行寫操作,耗費性能)。

鎖的粒度

  理論上,儘量鎖定需要修改的部分,而不是所有的數據,鎖定的數據單元越小,系統的併發控制度越高,比如行級鎖,修改的時候只鎖定這一行記錄,這個時候其他線程對該表的其他記錄修改不影響。但是,加鎖也是需要消耗資源的,鎖的各種操作:獲得鎖、檢查鎖狀態、釋放鎖等都會增加開銷,越細粒度的鎖開銷越大,過多的所操作所帶來的是性能急劇下降。
  所以我們要採用一種鎖策略來平衡併發度和系統性能,MySQL由於存儲引擎的插件式,每個存儲引擎可以實現自己的鎖策略,所以不需要通用的鎖策略,只需要在相應應用場景下選擇相應的存儲引擎即可。
  下面介紹兩種常用的鎖策略:表鎖和行級鎖。

表鎖

  MySQL最基本的鎖策略,顧名思義,對整張表加讀鎖和寫鎖。一般由MySQL服務器層實現,如果這個時候存儲引擎層還有鎖,優先表鎖。

行級鎖

  更大細粒度的鎖,只鎖住一行記錄,即對不同行記錄可併發操作。行級鎖只由存儲引擎層實現,MySQL服務器層沒有實現。

MVCC 多版本控制

  事務性存儲引擎(支持事務)一般不僅僅使用鎖來實現併發控制,因爲前面提到過,表鎖範圍太大,併發度不高,而行級鎖範圍太小,鎖操作開銷大,對那些讀多寫少的應用很不友好,所以纔有了MVCC。
  所謂的MVCC,即多版本控制,簡單地說就是寫操作前後是兩個版本,從而實現併發控制,至於具體實現如何,詳情參考:MVCC

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章