postgresql源码学习(十三)—— 行锁①-行锁模式与xmax
一、 四種行鎖?
1. 簡介與兼容性分析
? ? ? ?pg采用元組級常規鎖+xmax結合的方式實現行鎖。我們曾經提到過常規鎖是有很多類TAG的(typedef enum LockTagType),其中 LOCKTAG_TUPLE就是用來對元組加鎖的。
? ? ? ?不單純用元組級常規鎖,是為了避免事務修改行過多時,鎖表急劇增大導致性能劣化,并且鎖表在共享內存中的大小是有限的。因此,行鎖是不存儲在內存中的。
pg中通常有兩種方式會用到行鎖:
- 對行執行update,delete操作
- 顯式指定行鎖(例如select for update)
在新版本中,顯式加行鎖共有4種寫法
| 行鎖類型 | 簡介 |
| FOR UPDATE | FOR UPDATE會鎖定SELECT檢索到的行,就好像它們要被更新。 這可以阻止它們被其他事務鎖定、修改或者刪除,直到當前事務結束。 DELETE和修改主鍵、唯一鍵值的UPDATE會獲得這種鎖模式。 |
| FOR NO KEY UPDATE | 行為與FOR UPDATE類似,不過獲得的鎖較弱,不會阻塞SELECT FOR KEY SHARE。 不修改主鍵、唯一鍵值的UPDATE會獲得這種鎖模式。 |
| FOR SHARE | 行為與FOR NO KEY UPDATE類似,不過它在每個檢索到的行上獲得一個共享鎖而不是排他鎖。 讀該行,不允許對行進行更新 |
| FOR KEY SHARE | 行為與FOR SHARE類似,不過鎖更弱,不會被SELECT 讀該行鍵值,但允許對除鍵外的其他字段更新。在外鍵檢查時使用該鎖 |
4種行鎖兼容性如下
詳細
4種行鎖兼容性如下
詳細可參考 ?官方文檔第13章 13.3.2. Row-Level Locks
2. 對應源碼
它們對應的源碼在lockoptions.h文件
/** Possible lock modes for a tuple.*/ typedef enum LockTupleMode {/* SELECT FOR KEY SHARE */LockTupleKeyShare,/* SELECT FOR SHARE */LockTupleShare,/* SELECT FOR NO KEY UPDATE, and UPDATEs that don't modify key columns */LockTupleNoKeyExclusive,/* SELECT FOR UPDATE, UPDATEs that modify key columns, and DELETE */LockTupleExclusive } LockTupleMode;? ? ? ?如前所述,pg行鎖是由常規鎖+xmax結合實現的,因此它需要建立常規鎖與LockTupleMode之間的映射關系。
/** Each tuple lock mode has a corresponding heavyweight lock, and one or two* corresponding MultiXactStatuses (one to merely lock tuples, another one to* update them). This table (and the macros below) helps us determine the* heavyweight lock mode and MultiXactStatus values to use for any particular* tuple lock strength.** Don't look at lockstatus/updstatus directly! Use get_mxact_status_for_lock* instead.*/ static const struct {LOCKMODE hwlock; // 對應的常規鎖模式int lockstatus; // 顯式指定的鎖模式int updstatus; // 隱式指定的鎖模式 }tupleLockExtraInfo[MaxLockTupleMode + 1] = {{ /* LockTupleKeyShare */AccessShareLock, // 1級表鎖MultiXactStatusForKeyShare,-1 /* KeyShare does not allow updating tuples */},{ /* LockTupleShare */RowShareLock, // 2級表鎖MultiXactStatusForShare,-1 /* Share does not allow updating tuples */},{ /* LockTupleNoKeyExclusive */ExclusiveLock, // 7級表鎖MultiXactStatusForNoKeyUpdate,MultiXactStatusNoKeyUpdate},{ /* LockTupleExclusive */AccessExclusiveLock, // 8級表鎖MultiXactStatusForUpdate,MultiXactStatusUpdate} };? ? ? ? 其實這些行鎖模式的兼容性是怎么來的,就是根據對應的表鎖兼容性來的。例如一個delete操作,它在表上加的是3級鎖,但在行上加的是8級鎖。因此在表上該操作是兼容的(可以同時對一個表進行delete),但在行上是沖突的(不可以同時delete同一行)。
二、 xmax的設置
? ? ? ?通過前面的源碼可以看到,tupleLockExtraInfo中除了保存常規鎖對應的鎖模式,還保存了大量MultiXact中的鎖模式,MultiXact又是個什么呢?
1. MultiXactId
? ? ? ?通常如果只有一個事務增加行鎖,那么直接將行的xmax設為事務id,并在infomask中設置對應鎖類型即可。
? ? ? ?但select…for…語句中可以加行級共享鎖,即可以有多個事務對一個元組加共享鎖,這時就沒法通過將行的xmax設為事務id來表示了。為此,pg將多個事務組成一個mXactCacheEnt(multixact.c文件),并為其指定唯一的MultiXactId,此時在xmax處保存的就是MultiXactId。
typedef struct mXactCacheEnt {MultiXactId multi;int nmembers;dlist_node node;MultiXactMember members[FLEXIBLE_ARRAY_MEMBER]; } mXactCacheEnt;? ?為了區分xmax設置的是事務id還是MultiXactId,在使用MultiXactId時會在元組上增加HEAP_XMAX_IS_MULTI標記。
2. 模擬案例
會話1
create table t1(a int primary key,b int); insert into t1 values(1,1); select xmin,xmax,* from t1; -- 事務id 761會話2
Begin; select * from t1 for key share; -- 事務不提交會話1
select xmin,xmax,* from t1; -- 當前xmax中保存的是事務id(762)會話3
Begin; select * from t1 for share; -- 事務不提交(事務id 763)會話1
select xmin,xmax,* from t1; -- 當前xmax中保存的是MultiXactId怎么知道這個是MultiXactId?可以通過控制文件查看
pg_controldata –D $PGDATA | grep Multi3. 行鎖標記位
- 如果元組的xmax是事務id,需要通過infomask標記位區分元組的加鎖情況。
源代碼在htup_details.h,其實有很多種狀態,這里只簡單列一些
#define HEAP_XMAX_KEYSHR_LOCK 0x0010 /* for key share子句對應的鎖 */ #define HEAP_XMAX_EXCL_LOCK 0x0040 /* xmax is exclusive locker,排他鎖標記位 */ /* xmax is a shared locker */ #define HEAP_XMAX_SHR_LOCK (HEAP_XMAX_EXCL_LOCK | HEAP_XMAX_KEYSHR_LOCK) #define HEAP_XMAX_LOCK_ONLY 0x0080 /* xmax, if valid, is only a locker,顯式加行鎖 */ #define HEAP_XMAX_IS_MULTI 0x1000 /* t_xmax is a MultiXactId */ #define HEAP_KEYS_UPDATED 0x2000 /* tuple was updated and key cols */詳細可參考 ?https://mp.weixin.qq.com/s/nDBYbJpoBVqZxpYYaxPZ6Q
- 如果元組的xmax是MultiXactId,則每種子句都對應一種鎖模式(它們的對應關系通過tupleLockExtraInfo也可以看出來)
? ? ? ?為了保存MultiXactId和事務的映射關系,pg使用兩個SLRU進行分層映射,它們位于$PGDATA/pg_multixact目錄下,分別是offsets目錄和members目錄。
參考
《PostgreSQL技術內幕:事務處理深度探索》第2章
PostgreSQL鎖機制——行級鎖 - JavaShuo
https://www.modb.pro/db/70021
https://www.modb.pro/video/5128?sjhy
總結
以上是生活随笔為你收集整理的postgresql源码学习(十三)—— 行锁①-行锁模式与xmax的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: python中添加.pth_使用.pth
- 下一篇: PostgreSQL 中的系统字段:ta