問題的引入
我們首先將容納鎖的數據結構稱為鎖管理器。 鎖顯然是一個共享資源,因此添加一把鎖都需要將這個信息添加到鎖管理器中, 無論添加的鎖是什么類型, 添加的過程需要對鎖管理器添加一把排他鎖。這里說的鎖主要是重量級鎖,如 表鎖。 因此鎖管理器上很容易出現爭用。
非常常見的 SELECT/UPDATE/DELETE/INSERT 在表鎖上都不互斥。如果走上面的鎖管理器的邏輯成本就很高。 我們既想對這種常見的場景進行優化, 又要保證鎖的語義正確,那么我們必然要犧牲一些什么。 PostgreSQL 犧牲的是高級別鎖的加鎖效率。這通常是合理的,因為高級別的鎖非常不頻繁, 比如 DROP 需要高級別的鎖,但很少有應用會高并發運行drop.
那么如何來設計這樣的問題呢? 我直接粘貼chatgpt 對 lmgr/README 的部分翻譯, 然后再對設計要點進行一些補充。
Fast path lock原理
快速路徑鎖定是一種特殊的機制,旨在減少獲取和釋放某些被頻繁獲取和釋放鎖的開銷,并且這些很少發生沖突。
弱關系鎖。SELECT、INSERT、UPDATE 和 DELETE 在操作的每個關系上必須獲取鎖,以及各種可以在內部使用的系統目錄。許多 DML 操作可以在同一時間并行進行,而不沖突;只有如 CLUSTER、ALTER TABLE 或 DROP 這樣的 DDL 操作,或者用戶的顯式操作如 LOCK TABLE,才會與 DML 操作所獲取的“弱”鎖(AccessShareLock、RowShareLock、RowExclusiveLock)產生鎖沖突。
當前(早期PG 9.2)鎖定機制在處理這種工作負載時表現不佳。盡管鎖管理器的鎖是被分區的,但任何給定關系的鎖標識(locktag)仍然只屬于一個分區。因此,如果許多短查詢同時訪問同一張表,該分區的鎖管理器分區鎖就會成為一個爭用瓶頸。這種影響在配備 2 核處理器的服務器上就可以被測量出來,并且隨著核心數量的增加,這種現象會變得更加明顯。
為了減輕這個瓶頸,從 PostgreSQL 9.2 開始,每個后端進程被允許在其 PGPROC 結構中的數組內記錄有限數量的針對非共享關系的鎖,而不是使用主鎖表。在獲取鎖時,只有在鎖定者能夠驗證不存在沖突鎖的情況下,才能使用該機制。
這個算法的一個關鍵點是,必須能夠在不爭用共享的 LWLock 或自旋鎖的情況下驗證可能存在的沖突鎖的缺失。否則,這樣的做法只會將爭用瓶頸從一個地方移動到另一個地方。我們通過使用一個包含 1024 個整數計數器的數組來實現這一點,這實際上是對鎖空間的 1024 路分區。每個計數器記錄落入該分區的非共享關系上的“強”鎖(即,ShareLock、ShareRowExclusiveLock、ExclusiveLock 和 AccessExclusiveLock)的數量。
1. 弱鎖獲取前提: 當該計數器的值非零時,快速路徑機制將不能在該分區內獲取新的關系鎖。
2. 強鎖獲取過程: 一個強鎖持有者將增加計數器,然后掃描每個后端的數組以匹配快速路徑鎖;任何找到的鎖必須在嘗試獲取鎖之前轉移到主鎖表,以確保正確的鎖沖突和死鎖檢測。
理解重點:
1. 當我們需要對一張表加上弱鎖的時候,我們要看一下表上是否有強鎖(強鎖集中管理器),如果沒有,就不走Lock manager, 直接標記在一個更容易標記的地方(PGPROC)。 我們預期大部分時候都沒有強鎖。
2. 如果要加強鎖,首先在強鎖集中管理器中注冊這一事件,保證新的弱鎖不會出現,然后他就要檢查PGPROC, 看看有沒有在鎖管理器之外的弱鎖。 如果發現了, 要將若鎖遷移到鎖管理器內部, 因為死鎖檢測需要以來這個數據。
3. 強鎖集中管理器是的大小如何控制? 強鎖管理器僅僅有1024個空間,任何一把鎖都僅僅存放在其中的一個分區。 這理論上可能導致hash沖突,現實中應很很少發生, 即使發生了也沒有正確性問題, 影響僅僅是丟失了一次fast path 的機會。
4. 現在所有的鎖都需要和 強鎖管理器打交道,那么強鎖管理器的訪問會不會成為新的瓶頸,這個我們到下一篇文章再討論。
Fast Path 監控:
pg_locks.fastpath 屬性
Fast Path 限制
在PG17 之前, 代碼實現決定了一個事務中僅有16個弱鎖,在PG17 以后,這個值變成了 max_locks_per_transaction;
使用建議
PG 的表鎖通常都是在事務結束時釋放(并不是語句結束釋放)。 所以如果一個事務中訪問的鎖過多,依然會觸發上述的限制。 比如 有成千上百的子分區,大量的索引等。
有些query 可能最終只使用了一個索引,但在planning 階段需要訪問所有的索引去生成Plan, 這也可能浪費一些弱鎖。 這個問題可以通過 Plan Cache 來緩解,因為它避免了重復生成執行計劃。
同時減少索引, 減少分區個數,一個事務中的語句數量對這一主題都是有幫助的。