PostgreSQL 行鎖解讀

jesselyu發表於2015-06-14

    像其它資料庫一樣,PostgreSQL資料庫不僅有表級別的鎖,同時也有行級別的鎖。行鎖對於提高系統的並行度至關重要。那麼PostgreSQL是如何實現行鎖的呢?

PostgreSQL獲取行鎖的大致分為以下兩個階段:

1.獲取buffer page級別的鎖

   這是一個buffer content鎖,不是單純的buffer pin,當然buffer pin的次數肯定會增加。buffer content鎖分為share和exclusive兩種。”share”是共享鎖,

一般的讀操作,獲取的就是share鎖。”exclusive”是排它鎖,對記錄的修改,增加,刪除等需要獲取此類鎖。

   另外Buffer page又分為兩種,分別是local和shared。backend程式獨有的buffer是不需要鎖的,因為它只被本backend程式獨享,是local的。如臨時表等。

我們通常需要加鎖處理都是針對shared型別的buffer,不同程式之間對buffer page的操作需要透過鎖機制來保障。

   在對一個buffer page進行加鎖前,需要進行可性性判斷。即本條記錄是否是“HeapTupleInvisible”。如果是invisible的,是不能加鎖,這也是事務隔離的需要。

2.設定行鎖標誌位

  行鎖的實現比較複雜,我們不可能把所有需要上鎖的記錄都放到記憶體的lock table中,因為有些情況下,涉及到的記錄非常多。為了解決這個問題,通常只在

tuple的header中設定標記為來標識此行記錄已經被鎖。這兩個關鍵的標記為xmax和infomask。”xmax”放置當前事務的xid,“infomask”放置flag資訊。

設定infomask目的主要是為了區別與正常的deleted tuple分割槽開來,正常情況下xmax是用來標識被刪除的記錄。這樣一來,就不必去維護全域性級別的lock table了,

也可以實現任意記錄數的行鎖操作。另外如果是多個事務同時去上鎖一行記錄,那麼multixact就會被使用。

下面舉例說明:

image

上圖第一個紅色圈內容為一個事務,事務ID號為:15331854,所執行的語句為:

會話1:

postgres=# begin;
BEGIN
postgres=# update t1 set id=1 where id=2;
UPDATE 1

第二個紅色圈內容為另一個事務,事務ID號為:15331855,所執行的語句為:

會話2:

postgres=# begin;
BEGIN
postgres=# update t1 set id=2 where id=2;

可以看到,第二個會話hang住了,因為他們更新的是同一個表的同一條記錄。

我們再起會話3:

image

看到這條記錄的xmax已經標記為第一個事務的xid,即:15331854。這也驗證了上面的說法,當update時,PG會去標記每條記錄的xmax,設定為當前xid。

另外一個標記位,從這裡沒有辦法看到。需要dump出這條記錄的header才能看到。

 

我們再在會話1中跑語句:select xmin,xmax,cmin,cmax ,t.* from t1 t;

image

細心的同學發現,情況不對,跟前面會話3查出的結果完全不同。那麼是PG錯了嗎?

當然不是的,這就是MVCC實現的結果。會話3中看到的那條記錄是old tuple,也就是老的記錄,因為會話1還沒有提交,而會話1看到的是新的記錄。

根據read commit隔離級別,本事務中之前update掉的記錄,是允許看到的。另外,我們發現xmin的xid已經是本事務的xid了。

 

總結:

PG對於update其實也生成了一個新版本的tuple,在老版本的tuple header中做了兩個標記:一個是xmax,另外一個是infomask。

“xmax”放持有此記錄lock的xid,infomask存放flag資訊。

infomask的flag資訊如下:

image

 

另外對於delete操作和update操作,PG做了不同處理。具體主要表現在索引上。

那麼行鎖與索引有什麼關係? 大家知道PG索引是沒有MVCC的,那麼PG如何處理這些細節呢,下次再詳細講述。

來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/30088583/viewspace-1699315/,如需轉載,請註明出處,否則將追究法律責任。

相關文章