PG數據庫的鎖咋弄得這麼複雜呢

語言: CN / TW / HK

談到鎖,很多朋友和我抱怨過PG的鎖的對外展現太過於複雜,很多初學者根本搞不懂,不像其他數據庫行鎖、表鎖,一目瞭然。Postgresql數據庫是一種十分學院派的數據庫,因為從本質上講,PG是一種對象-關係型數據庫,所以PG數據庫的底層完全按照對象數據庫來設計。為了貫徹對象特性,PG鎖的基礎是對象鎖,所以從DBA的角度來看,就很複雜了。

如果你是一個Oracle DBA,那麼你可能習慣於看dba_locks這樣的視圖,Oracle的鎖劃分的很細,有上百種鎖,不過Oracle的鎖機制的底層是統一的,都是Enqueue,所以從監控視圖上,Oracle的所有鎖都是統一的。Oracle的鎖使用類型(TYPE),持有模式(MODE_HELD),申請模式(MOD_REQUESTED),參數1(LOCK_ID1),參數2(LOCK_ID2)等幾個有限的字段統一表示所有的鎖類型。

從DBA_LOCKS視圖的結構上可以看出,Oracle把繁瑣的鎖用一個十分簡單的數據結構來描述了。

相對而言,PG的pg_locks視圖就要複雜的多,我們來看pg_locks這個PG用來展現鎖情況的視圖:

除了locktype和Oracle類似外,其他的字段都讓人感到有些複雜。實際上這個和PG數據庫的對象數據庫的特性是有關係的。如果從對象層面來看,需要進行串行化處理的對象層次十分複雜,正是因為PG鎖考慮了對象級的多個層面的東西,把所有的需要串行化的需求都歸納到有限的幾類鎖中(相對於Oracle鎖的種類有上百種),因此要想把PG的鎖完全統一起來並不容易。

我以前對PG鎖的理解也比較膚淺,因為在絕大多數系統中,我們只需要關注事務鎖就可以了。不過PG的事務鎖也不那麼直觀,可能我們在pg_locks中會看到一些讓人感到十分困惑的數據。

從我們現在的一個純粹交易型的測試環境中,我們看到鎖的類型有5種。

這些鎖到底是什麼含義呢?到底哪個才是行鎖呢?是tuple嗎?直到我認真的閲讀了俄羅斯postgrepro公司的PG大師葉格爾-羅格夫關於PG鎖的系列文章,才算從根兒上理解了PG的鎖。從此再看到亂七八糟的PG鎖就變得十分舒服和親切了。從今天開始,我分幾期把從葉格爾大師那裏學到的知識給大家分享一下。

PG的對象級鎖有很多種,上面是葉格爾文章中的截圖,列出了PG中常見的對象級鎖。對於鎖的類型,不同的PG版本可能鎖的種類略有不同。從等待事件中我們可以看到一些種類的鎖產生的等待事件:

從廣義上講,PG把實現串行化的互斥機制都稱為鎖,所以PG鎖實際上融合了Oracle數據庫的ENQUEUE/LATCH/MUTEX等機制,所以相當複雜。當然大多數串行化共享內存操作的PG鎖都使用了類似LATCH的LWLOCK,不過二者之間並不是完全等同的。要觀察這些鎖的情況,我們都可以通過pg_locks這個視圖。因為不同的鎖的實現方式都會不同,不同類型的鎖的參數也不相同,所以pg_locks中的字段很多。有時候甚至會引起我們對行鎖的分析都出現困難。關於行鎖的詳細分析今天因為篇幅問題,可能沒有時間展開了,我會在後續的系列文章中一點點的展開討論。以前在Percona上看到過一篇文章,裏面有一個解析pg_locks的SQL語句。

SELECT pid, virtualtransaction AS vxid, locktype AS lock_type,mode AS lock_mode, granted,

CASE

WHEN virtualxid IS NOT NULL AND transactionid IS NOT NULL THEN virtualxid || ' ' || transactionid

WHEN virtualxid::text IS NOT NULL

THEN virtualxid

ELSE

transactionid::text 

END AS xid_lock, relname,

page, tuple, classid, objid, objsubid

FROM pg_locks LEFT OUTER JOIN pg_class ON (pg_locks.relation = pg_class.oid)

WHERE  pid != pg_backend_pid();

用這條語句來解析PG_LOCKS後,我們看這些鎖數據就舒服多了。下面來看一個例子:

關於virtualxid和TransactionID的區別我們明天的文章中再來分析,不同的鎖的類型其參數是不同的。relation鎖的是relations對象級別上的鎖,用於保證某個relation在被訪問時不被改變或者刪除,而PG的事務鎖(比如transactionid鎖 )都是針對事務的。

如果你以前是Oracle DBA那麼理解PG鎖的障礙來自於PG鎖的類型劃分與Oracle在維度上的不同。Oracle是按照功能來劃分鎖的,比如行鎖(TX)、表鎖(TM)、表空間鎖(TT)、SEQUENCE鎖(SQ)。而PG的鎖是按照對象的粒度來劃分的,把相同級別的不同對象劃分為同類。除了行鎖(transactionid)之外,其他鎖的處理模式都是類似的,因為行鎖的併發量巨大,因此針對行鎖採用了獨特的處理方式,以避免性能問題。

正是因為PG的鎖十分複雜,因此今天我們重點來討論關係型鎖這種和運維與應用開發關係最為密切的鎖。要了解關係級鎖的細節,一定要首先了解鎖的模式,PG的關係級鎖有着相當複雜的鎖模式,下面這個表格可以讓我們快速理解複雜的鎖模式。

Access Share是最弱級別的鎖,這種鎖是由select操作產生的,而Access Exclusive是最嚴格的鎖,如果在關係上執行DROP/TRUNCATE等操作,會產生這種鎖。我們如何來理解這張表呢?用一個最為簡單的CREATE INDEX來做個示範吧。我們看到CREATE INDEX會持有SHARE模式的鎖,這個模式兼容Access Share、Row Share和SHARE,因此多個CREATE INDEX可以併發執行,同時SELECT和SELECT FOR UPDATE操作可以不受阻塞執行。而INSERT/DELETE/UPDATE操作需要Row Exclusive模式的鎖,會受到阻止。

今天因為時間關係,就先講到這裏吧,看到這裏的朋友可能現在還是有點懵圈,不知道老白到底想要講什麼。我想把這個系列全部看完之後,一切都會明瞭的。明天南瑞要搞兩年一度的子衿IT基礎架構大會,我一大早要去參會,如果今天下午有時間,我會把第二部分寫出來,明早發出。看完第二部分,大家就會明白我今天為什麼要寫這一篇了。