Postgresql

並發創建 Postgres 索引時需要什麼類型的鎖?

  • December 6, 2020

我知道同時創建索引時,只需要一個SHARE ShareUpdateExclusiveLock 鎖。

但是,在流程開始的一小段時間內是否需要更嚴格的鎖定類型?還是整個操作只需要一個SHARE ShareUpdateExclusiveLock 鎖?

我問是因為我想知道在同時創建索引時設置哪些類型的超時是有利的。

好吧,當文件不足時,可以直接查看程式碼。為了避免將來可能的行號更改,我將提供已發布PostgreSQL 13.1(標籤)的連結。REL_13_1

我可能不會描述來自客戶端的命令的整個程式碼路徑。通過語法,我們知道我們需要IndexStmt命令節點——並發和非並發創建索引的語法相同。一切有用的開始ProcessUtilitySlow

在這裡,在評論中case T_IndexStmt,可以找到問題的直接答案:

/*
* .... To avoid lock upgrade hazards, it's
* important that we take the strongest lock that will
* eventually be needed here, so the lockmode calculation
* needs to match what DefineIndex() does.
*/
lockmode = stmt->concurrent ? ShareUpdateExclusiveLock
   : ShareLock;

一般來說,PostgreSQL 在命令執行期間不會升級鎖。並將採取最終需要的最強鎖。因為CREATE INDEX CONCURRENTLYShareUpdateExclusiveLock。不是SHARE問題中提到的鎖。

但讓我們繼續閱讀:

  • 在分區表的情況下,find_all_inheritors繼承者將獲得相同的鎖定模式。
  • intransformIndexStmt可以找到一些鎖定模式,但是NoLock-AccessShareLock它們比ShareUpdateExclusiveLock
  • DefineIndex我們看到另一個提醒:
    * To avoid lock upgrade hazards, that lock should be at least
    * as strong as the one we take here.

並決定再次使用 ShareUpdateExclusiveLock 進行並發建構。這部分程式碼明顯很長(仍然沒有規劃器那麼大),但可讀。


我錯過了什麼?大概。因此,讓我們建構帶有-DLOCK_DEBUG調試鎖定行為選項的 PostgreSQL,看看會發生什麼:

./configure  --prefix=/home/melkij/tmp/pgdev/inst --enable-cassert --enable-debug CFLAGS="-ggdb -Og -g3 -fno-omit-frame-pointer -DLOCK_DEBUG" --enable-tap-tests
make -sj 4
make install
initdb ...
pg_ctl start ...
# then psql with follow commands
create table foo as select generate_series(1,1000) as id;
set trace_locks = true;
create index concurrently on foo (id);
select oid, relname, relkind from pg_class where oid in (16387,16393);
#  oid  |  relname   | relkind 
#-------+------------+---------
# 16387 | foo        | r
# 16393 | foo_id_idx | i

trace_locks會記錄很多東西,但我們感興趣的是:

[vxid:3/62 txid:0] [CREATE INDEX] LOG:  LockAcquire: lock [12664,16387] ShareUpdateExclusiveLock
[vxid:3/62 txid:0] [CREATE INDEX] LOG:  LockAcquire: lock [12664,16387] ShareUpdateExclusiveLock
[vxid:3/62 txid:0] [CREATE INDEX] LOG:  LockAcquire: lock [12664,16393] AccessExclusiveLock
[vxid:3/62 txid:495] [CREATE INDEX] LOG:  LockAcquire: lock [12664,16387] ShareUpdateExclusiveLock
[vxid:3/63 txid:0] [CREATE INDEX] LOG:  LockAcquire: lock [12664,16387] ShareUpdateExclusiveLock
[vxid:3/63 txid:0] [CREATE INDEX] LOG:  LockAcquire: lock [12664,16393] RowExclusiveLock
[vxid:3/64 txid:0] [CREATE INDEX] LOG:  LockAcquire: lock [12664,16387] ShareUpdateExclusiveLock
[vxid:3/64 txid:0] [CREATE INDEX] LOG:  LockAcquire: lock [12664,16393] RowExclusiveLock
[vxid:3/64 txid:0] [CREATE INDEX] LOG:  LockAcquire: lock [12664,16393] ExclusiveLock
[vxid:3/64 txid:0] [CREATE INDEX] LOG:  LockAcquire: lock [12664,16393] ExclusiveLock

lock [12664,16387]這裡的意思是 database OID = 12664pg_class OID = 16387(這些行是從 記錄的LockAcquireExtended)。

所以,我們實際上只ShareUpdateExclusiveLock在表本身上獲取。OID=16393與其他幾個鎖是該命令建立的索引。儘管針對此對象提到了重鎖定級別,但它不會干擾其他查詢。Create index concurrently命令專門設計用於在執行正常的應用程序查詢時安全工作。

引用自:https://dba.stackexchange.com/questions/280284