鎖(locking)🔒

解決問題:

一方面要最大程度地利用數據庫的併發訪問,另一方面還要確保每個用户能以一致的方式讀取和修改數據

一.什麼是鎖🥖

1.鎖是數據庫系統區別於文件系統的一個關鍵特性

2.鎖機制用於管理對共享資源的併發訪問

3.數據庫使用鎖是為了支持對共享資源進行併發訪問,提供數據的完整性一致性

二.lock與latch🍞

latch

1.latch一般稱為閂鎖(輕量級的鎖),因為其要求鎖定的時間必須非常短,長了性能會非常差

2.在InnoDB中,latch分為mutex(互斥量)rwlock(讀寫鎖)

3.其目的是用來保證併發線程操作臨界資源的正確性,並且通常沒有死鎖檢測的機制

lock

1.lock的對象是事務,用來鎖定的是數據庫中的對象,如表、頁、行

2.一般lock的對象僅在事務commit或rollback後進行釋放(注意不同事務隔離級別不同)

3.有死鎖機制

pg數據庫AccessShareLock是什麼所類型_數據

三.InnoDB存儲引擎中的鎖🍰

1.鎖的類型

兩種標準的行級鎖

共享鎖(S Lock)允許事務讀一行數據

排他鎖(X Lock)允許事務刪除或更新一行數據

-鎖兼容(Lock Compatible):事務T1 已獲得行r的S鎖、T2可立即獲得行r的共享鎖,因為讀取並不會改變行r的數據

-鎖不兼容:T3想獲得行r的排他鎖,則其必須等待事務T1、T2釋放行r上的共享鎖


pg數據庫AccessShareLock是什麼所類型_數據_02

InnoDB支持多粒度(granular)鎖定

①這種鎖定允許事務在行級上的鎖和表級上的鎖同時存在,由意向鎖實現

意向鎖(Intention Lock),將鎖定的對象分為多個層次,意向鎖意味着事務希望在更細粒度(fine granularity)上進行加鎖,將上鎖的對象看成一棵樹,那麼對最下層的對象上鎖,也就是對最細粒度的對象進行上鎖,那麼首先對粗粒度的對象上鎖

③InnoDB其意向鎖即為表級別的鎖,設計目的是為了在一個事務中揭示下一行將被請求的所類型

(1)意向共享鎖(IS Lock),事務想要獲得一張表中某幾行的共享鎖

(2)意向排他鎖(IX Lock),事務想要獲得一張表中某幾行的排他鎖

pg數據庫AccessShareLock是什麼所類型_死鎖_03


2.一致性非鎖定讀

一致性的非鎖定讀(consistent nonlocking read)是指InnoDB存儲引擎通過多行版本控制(multi versioning)的方式來讀取當前執行時間數據庫中行的數據


pg數據庫AccessShareLock是什麼所類型_死鎖_04

①之所以稱為非鎖定讀,因為不需要等待訪問的行上X鎖的釋放,而會去讀取行的一個快照數據,而快照數據的實現是通過undo段來完成,而undo用來在事務中回滾數據,因此快照數據本身沒有額外開銷,而且讀取快照數據是不需要上鎖的

②一個行記錄可能不只有一個快照數據,一般稱這種技術為行多版本技術。由此帶來的併發控制,稱之為多版本併發控制(Multi Version Concurrency Control,MVCC)

③不同事務隔離級別下的非鎖定的一致性讀,READ COMMITED,讀被鎖定行最新一份快照數據;REPEATABLE READ,讀事務開始時的行數據版本


3.一致性鎖定讀

InnoDB存儲引擎對於SELECT語句支持兩種一致性的鎖定讀(locking read)操作:

①SELECT...FOR UPDATE

對讀取的行記錄加一個X鎖,其他事務不能對已鎖定的行加上任何鎖

②SELECT... LOCK IN SHARE MODE

對讀取的行記錄即一個S鎖,其他事務可向被鎖定的行加S鎖,但如果加X鎖,則會被阻塞

③此外以上兩種操作必須在一個事務中,但事務提交了,鎖也就釋放了,因此在使用上述語句SELECT鎖定語句時,務必加上BEGIN,START TRANSCATION或者SET AUTOCOMMIT=0


4.自增長與鎖

一種主鍵方式,在InnoDB內存結構中,對每個含有自增長值的表都有一個自增長計算器(auto-increment counter),當進行插入操作時,這個計數器會被初始化

①AUTO-INC Locking ,插入操作會依據這個自增長的計數器值加1賦予自增長列,一種特殊的表鎖機制,為提高插入性能,鎖不是在事務完成後釋放,而是在完成對自增長插入的SQL語句後立即釋放

缺點:1)對有自增長值得併發插入性能較差,事務必須等待前一個插入的完成

2)對於INSERT...SELECT的大數據量的插入會影響插入的性能,因為另一個事務中的插入會被阻塞

②插入類型

pg數據庫AccessShareLock是什麼所類型_數據_05


③在InnoDB中,自增長的列必須是索引,同時必須是索引的第一個列


5.外鍵和鎖

①外鍵主要用於引用完整性的約束檢查檢查

②對於外鍵值得插入或更新,首先需要查詢父表中得記錄,即SELECT父表。但是對於父表的SELECT操作,不使用一致性非鎖定讀的方式,因為這樣會發生數據不一致的問題,因此這時使用SELECT...LOCK SHARE MODE方式

四.鎖的算法🍯

1.行鎖的3種算法

Record Lock:單個記錄上的鎖

Gap Lock:間隙鎖,鎖定一個範圍,但不包含記錄本身

Next-Key Lock: Gap Lock + Record Lock,鎖定一個範圍,並且鎖定記錄本身。其設計目的就是為了解決Phantom Problem ❔,它鎖定的是一個範圍,是謂詞鎖(predict lock)的一種改進,當單查詢的索引含有唯一屬性時,InnoDB存儲引擎會優化N-K L降級為Record Lock,即鎖住索引本身


pg數據庫AccessShareLock是什麼所類型_數據庫_06


④Gap Lock的作用是為了組織多個事務將記錄插入到同一範圍內,而這會導致Phantom Problem問題的產生,用户可顯示關閉Gap Lock

(1)將事務的隔離級別設置為READ COMMITED

(2)將參數innodb_locks_unsafe_for_binlog設置為1

但這樣的設置破壞了事務的隔離性,而且對於replication,可能會導致主從數據不一致,性能也不會比READ REPEATABLE好

2.解決Phantom Problem❔

①在默認隔離級別下,即REPEATABLE READ下,InnoDB採用Next-Key Lock機制來避免Phantom Problem(幻像問題)

Phantom Problem(幻像問題)它是指在同一事務下,連續執行兩次同樣的SQL語句可能導致不同的結果,第二次的SQL語句可能會返回之前不存在的行



五.鎖問題🎂

因為事務隔離性的要求,鎖只會帶來三種問題,如果可以這三種情況的發生,那件不會產生併發異常

1.髒讀(Dirty Read)

①與髒頁區分開:

1)髒頁是在緩衝池中已經被修改的頁,但還沒有刷新的磁盤中,即數據庫實例內存中的頁和磁盤中的頁的數據是不一致的,當然在刷新磁盤之前,日誌都已經被寫入到了重做日誌中

而且髒頁並不影響數據的一致性(兩者最終會達到一致,髒頁都會刷回到磁盤),並且髒頁的刷新是異步的,不影響數據庫的可用性,因此可能帶來性能的提高

2)而所謂髒數據是指事務對緩衝池中行記錄的修改,並且沒有被提交(commit)

如果讀到了髒數據,即一個事務可以讀到另一個事務中未提交的數據,則顯然違反了數據庫的隔離性

②發生條件

在生產環境中不常發生,發生條件所需事務隔離級別為READ UNCOMMITTED,而絕大部分數據庫至少設置成READ COMMITED,InnoDB默認為READ REPEATABLE

2.不可重複讀

①不可重複讀是指在一個事務內多次讀取同一數據集合,在這個事務還沒有結束時,另外一個事務也訪問該同一數據集合,並做了一些DML操作。

因此在第一個事務中的兩次讀數據之間,由於第二個事務的修改,導致第一個事務兩次讀到的數據可能不一致的情況,這種情況稱為不可重複讀

②不可重複讀和髒讀的區別:髒讀是讀到未提交的數據,而不可重複讀讀到的卻是已經提交的數據,但是其違反了數據庫事務一致性的要求

③很多數據庫廠商(Oracle、Microsoft SQL Server)默認隔離級別READ COMMITTED下允許不可重複讀現象

④在InnoDB中,用Next-Key Lock來避免不可重複讀的問題(MySQL官方定義為Phantom Problem),即幻像問題。在NKL下,對於索引的掃描,不僅是鎖住掃描到的索引,而且還鎖住這些索引覆蓋的範圍(gap)。因此在這個範圍內的插入都是不允許的,這樣就避免了另外的事務的數據插入帶來的不可重複讀問題。

因此InnoDB隔離級別默認使用READ REPEATABLE,採用Next-Key Lock算法

3.丟失更新

丟失更新是另一個鎖導致的問題,簡單來説就是一個事務的更新操作會被另一個事務的更新操作所覆蓋,從而導致數據的不一致,在當前數據庫的任何隔離級別下,都不會導致數據庫理論意義上的丟失更新問題

但在生產應用中還有另一個邏輯意義上的丟失更新問題(銀行轉賬問題)


pg數據庫AccessShareLock是什麼所類型_數據庫_07

①如何避免?

需讓事務在這種情況下的操作變成串行化,而不是並行的操作

示例:

pg數據庫AccessShareLock是什麼所類型_死鎖_08

六.阻塞🍮

因為不同鎖的兼容性關係,在有些時刻一個事務中的鎖需要等待另一個事務中的鎖釋放它所佔用的資源,阻塞不是壞是,其是為了確保事務可以併發且正常地運行

①innodb_lock_wait_timeout用來控制等待的時間(默認50秒)

②innodb_rollback_on_timeout用來設定是否在等待超時時對進行中的事務進行回滾操作(默認OFF,代表不回滾),參數innodb_lock_wait_timeout是動態的

七.死鎖🍩

1.死鎖概念

死鎖是指兩個或以上的事務在執行過程中,因為爭奪資源而造成的一種互相等待的現象

①解決方法

(1)最簡單的一種方法超時,即當兩個事務互相等待時,當一個等待時間超過設置的某一閾值,其中一個事務進行回滾,另一個等待的事務就能繼續進行,用innodb_lock_wait_timeout設置超時時間

(2)採用wait-for graph(等待圖)方式來進行死鎖檢測

它要求數據庫保存:1)鎖的信息鏈表 2)事務等待鏈表

而在途中,事務T1指向T2邊的定義為:

1)事務T1等待事務T2所佔用的資源

2)事務T1最終等待T2所佔用的資源,也就是事物之間等待相同的資源,而事務T1發生在事務T2後面

通過上述鏈表構造出一張圖,若這個圖中存在迴路,就代表存在死鎖,因此資源間相互發生等待

pg數據庫AccessShareLock是什麼所類型_死鎖_09

若存在死鎖,通常來説InnoDB存儲引擎選擇回滾undo量最小的事務

wait-for graph檢測通常採用深度優先的算法實現,1.2之前,都是採用遞歸方式實現,1.2後,將遞歸用非遞歸的方式實現,從而提高InnoDB存儲引擎的性能

2.死鎖概率


pg數據庫AccessShareLock是什麼所類型_死鎖_10

死鎖概率與以下幾點因素有關:

①系統中事務的數量(n),數量越多發生死鎖的概率越大

②每個事務操作的數量(r),每個事務操作的數量越多,發生死鎖的概率越大

③操作數據的集合(R),越小則發生死鎖的概率越大

八.鎖升級(Lock Escalation)🍨

鎖升級是指當前鎖的粒度降低,例如,數據庫可以把一個表的1000個行級鎖升級為頁鎖,或者將頁鎖升級為表鎖

InnoDB存儲引擎不存在鎖升級的問題,因為其不是根據每個記錄來產生行鎖的,相反,其根據每個事務訪問的每個頁對鎖進行管理的,採用的是位圖的方式,因此不管一個事務鎖住頁中一個記錄還是多個記錄,其開銷都是一致的