SpinLock

使用tas实现的自旋锁。

  while (TAS_SPIN(lock))
  {
    perform_spin_delay(&delayStatus);
  }

perform_spin_delay(SpinDelayStatus *status)
{
  /* CPU-specific delay each time through the loop */
  SPIN_DELAY();

  /* Block the process every spins_per_delay tries */
  if (++(status->spins) >= spins_per_delay)
  {
    if (++(status->delays) > NUM_DELAYS)
      s_lock_stuck(status->file, status->line, status->func);

    if (status->cur_delay == 0) /* first time to delay? */
      status->cur_delay = MIN_DELAY_USEC;

    pg_usleep(status->cur_delay);

    /* increase delay by a random fraction between 1X and 2X */
    status->cur_delay += (int) (status->cur_delay *
                  pg_prng_double(&pg_global_prng_state) + 0.5);
    /* wrap back to minimum delay when max is exceeded */
    if (status->cur_delay > MAX_DELAY_USEC)
      status->cur_delay = MIN_DELAY_USEC;

    status->spins = 0;
  }
}

如果没有tas,则使用信号量实现,属于最底层的锁

LWLock

https://zhmin.github.io/posts/postgresql-lwlock/ 文档描述清楚

  1. lock状态保存在共享内存中
  2. 共享变量是在竞争lock失败之后尝试PGSemaphoreLock,此时sem是默认值为0,所以会等待
  3. 本质上lock是同一块内存,可以调试查看地址,只是对内存使用信号量进行控制
  4. LWLock会进行lock竞争,有等待队列,但是没有死锁检测

常规Locks

使用lw实现

  1. 共有下面8种,

#0  LockAcquireExtended (locktag=0x7ffe04c94b00, lockmode=5, sessionLock=false, dontWait=true, reportMemoryError=true, locallockp=0x7ffe04c94af8) at /home/esoye/postgres/src/backend/storage/lmgr/lock.c:1145
#1  0x00005563f316862d in ConditionalLockRelationOid (relid=16385, lockmode=5) at /home/esoye/postgres/src/backend/storage/lmgr/lmgr.c:160
#2  0x00005563f2da006e in RangeVarGetRelidExtended (relation=0x5563f3738618, lockmode=5, flags=2, callback=0x5563f2e9ae66 <RangeVarCallbackForLockTable>, callback_arg=0x5563f37386b8) at /home/esoye/postgres/src/backend/catalog/namespace.c:390
#3  0x00005563f2e9adb6 in LockTableCommand (lockstmt=0x5563f37386a8) at /home/esoye/postgres/src/backend/commands/lockcmds.c:55
#4  0x00005563f3190b97 in standard_ProcessUtility


#define AccessShareLock         1  /* SELECT */                            // 可以正常访问,除非删表等操作才会阻塞,其他操作不阻塞,需要调研事务细节
#define RowShareLock            2  /* SELECT FOR UPDATE/FOR SHARE */       // for share,意向锁,实际上是锁tuple,只是为了避免某些操作可能需要遍历全表时对表加锁,目的是提前检测
#define RowExclusiveLock        3  /* INSERT, UPDATE, DELETE */            // 意向排他锁,正常数据变动会加这个锁。
#define ShareUpdateExclusiveLock  4  /* VACUUM (non-FULL), ANALYZE, CREATE INDEX CONCURRENTLY */            
#define ShareLock               5  /* CREATE INDEX (WITHOUT CONCURRENTLY) */
#define ShareRowExclusiveLock   6  /* like EXCLUSIVE MODE, but allows ROW SHARE */
#define ExclusiveLock           7  /* blocks ROW SHARE/SELECT...FOR UPDATE */
#define AccessExclusiveLock     8  /* ALTER TABLE, DROP TABLE, VACUUM FULL, and unqualified LOCK TABLE */


static const LOCKMASK LockConflicts[] = {
  0,

  /* AccessShareLock */
  LOCKBIT_ON(AccessExclusiveLock),

  /* RowShareLock */
  LOCKBIT_ON(ExclusiveLock) | LOCKBIT_ON(AccessExclusiveLock),

  /* RowExclusiveLock */
  LOCKBIT_ON(ShareLock) | LOCKBIT_ON(ShareRowExclusiveLock) | LOCKBIT_ON(ExclusiveLock) | LOCKBIT_ON(AccessExclusiveLock),

  /* ShareUpdateExclusiveLock */
  LOCKBIT_ON(ShareUpdateExclusiveLock) | LOCKBIT_ON(ShareLock) | LOCKBIT_ON(ShareRowExclusiveLock) | LOCKBIT_ON(ExclusiveLock) | LOCKBIT_ON(AccessExclusiveLock),

  /* ShareLock */
  LOCKBIT_ON(RowExclusiveLock) | LOCKBIT_ON(ShareUpdateExclusiveLock) |
  LOCKBIT_ON(ShareRowExclusiveLock) |
  LOCKBIT_ON(ExclusiveLock) | LOCKBIT_ON(AccessExclusiveLock),

  /* ShareRowExclusiveLock */
  LOCKBIT_ON(RowExclusiveLock) | LOCKBIT_ON(ShareUpdateExclusiveLock) |
  LOCKBIT_ON(ShareLock) | LOCKBIT_ON(ShareRowExclusiveLock) |
  LOCKBIT_ON(ExclusiveLock) | LOCKBIT_ON(AccessExclusiveLock),

  /* ExclusiveLock */
  LOCKBIT_ON(RowShareLock) |
  LOCKBIT_ON(RowExclusiveLock) | LOCKBIT_ON(ShareUpdateExclusiveLock) |
  LOCKBIT_ON(ShareLock) | LOCKBIT_ON(ShareRowExclusiveLock) |
  LOCKBIT_ON(ExclusiveLock) | LOCKBIT_ON(AccessExclusiveLock),

  /* AccessExclusiveLock */
  LOCKBIT_ON(AccessShareLock) | LOCKBIT_ON(RowShareLock) |
  LOCKBIT_ON(RowExclusiveLock) | LOCKBIT_ON(ShareUpdateExclusiveLock) |
  LOCKBIT_ON(ShareLock) | LOCKBIT_ON(ShareRowExclusiveLock) |
  LOCKBIT_ON(ExclusiveLock) | LOCKBIT_ON(AccessExclusiveLock)

};

typedef enum LockTagType {
  LOCKTAG_RELATION,            /* whole relation */
  LOCKTAG_RELATION_EXTEND,     /* the right to extend a relation */
  LOCKTAG_DATABASE_FROZEN_IDS, /* pg_database.datfrozenxid */
  LOCKTAG_PAGE,                /* one page of a relation */
  LOCKTAG_TUPLE,               /* one physical tuple */
  LOCKTAG_TRANSACTION,         /* transaction (for waiting for xact done) */
  LOCKTAG_VIRTUALTRANSACTION,  /* virtual transaction (ditto) */
  LOCKTAG_SPECULATIVE_TOKEN,   /* speculative insertion Xid and token */
  LOCKTAG_OBJECT,              /* non-relation database object */
  LOCKTAG_USERLOCK,            /* reserved for old contrib/userlock code */
  LOCKTAG_ADVISORY             /* advisory user locks */
} LockTagType;

https://blog.csdn.net/hyman_c/article/details/119666570

  1. 普通共享锁普通排他锁 锁检测在LockAcquireExtended函数中
  2. 访问共享锁访问排他锁

  1. 直接select 的时候会在analyze阶段对表加AccessShareLock,此时会进行bind操作,所以会open relation
#0  0x00005563f2c95ec4 in relation_openrv_extended (relation=0x5563f3738708, lockmode=1, missing_ok=true) at /home/esoye/postgres/src/backend/access/common/relation.c:186
#1  0x00005563f2d32f35 in table_openrv_extended (relation=0x5563f3738708, lockmode=1, missing_ok=true) at /home/esoye/postgres/src/backend/access/table/table.c:108
#2  0x00005563f2e2d02e in parserOpenTable (pstate=0x5563f3738928, relation=0x5563f3738708, lockmode=1) at /home/esoye/postgres/src/backend/parser/parse_relation.c:1367
#3  0x00005563f2e2d29b in addRangeTableEntry (pstate=0x5563f3738928, relation=0x5563f3738708, alias=0x0, inh=true, inFromCl=true) at /home/esoye/postgres/src/backend/parser/parse_relation.c:1443
#4  0x00005563f2e0a17d in transformTableEntry (pstate=0x5563f3738928, r=0x5563f3738708) at /home/esoye/postgres/src/backend/parser/parse_clause.c:395
#5  0x00005563f2e0bc36 in transformFromClauseItem (pstate=0x5563f3738928, n=0x5563f3738708, top_nsitem=0x7ffe04c94d90, namespace=0x7ffe04c94d98) at /home/esoye/postgres/src/backend/parser/parse_clause.c:1066
#6  0x00005563f2e09a29 in transformFromClause (pstate=0x5563f3738928, frmList=0x5563f3738750) at /home/esoye/postgres/src/backend/parser/parse_clause.c:132
#7  0x00005563f2dd1175 in transformSelectStmt (pstate=0x5563f3738928, stmt=0x5563f37387b0) at /home/esoye/postgres/src/backend/parser/analyze.c:1313
#8  0x00005563f2dcf606 in transformStmt (pstate=0x5563f3738928, parseTree=0x5563f37387b0) at /home/esoye/postgres/src/backend/parser/analyze.c:365
#9  0x00005563f2dcf4ad in transformOptionalSelectInto (pstate=0x5563f3738928, parseTree=0x5563f37387b0) at /home/esoye/postgres/src/backend/parser/analyze.c:305
#10 0x00005563f2dcf3a3 in transformTopLevelStmt (pstate=0x5563f3738928, parseTree=0x5563f37388b8) at /home/esoye/postgres/src/backend/parser/analyze.c:255
#11 0x00005563f2dcf093 in parse_analyze_fixedparams (parseTree=0x5563f37388b8, sourceText=0x5563f3737c50 "select * from t1;", paramTypes=0x0, numParams=0, queryEnv=0x0) at /home/esoye/postgres/src/backend/parser/analyze.c:123
#12 0x00005563f3186c9d in pg_analyze_and_rewrite_fixedparams (parsetree=0x5563f37388b8, query_string=0x5563f3737c50 "select * from t1;", paramTypes=0x0, numParams=0, queryEnv=0x0) at /home/esoye/postgres/src/backend/tcop/postgres.c:643
#13 0x00005563f31874a7 in exec_simple_query (query_string=0x5563f3737c50 "select * from t1;") at /home/esoye/postgres/src/backend/tcop/postgres.c:1152