PostgreSQL如何获取事务号XID
这篇文章主要为大家展示了“PostgreSQL如何获取事务号XID”,内容简而易懂,条理清晰,希望能够帮助大家解决疑惑,下面让小编带领大家一起研究并学习一下“PostgreSQL如何获取事务号XID”这篇文章吧。
创新互联公司坚持“要么做到,要么别承诺”的工作理念,服务领域包括:网站建设、网站制作、企业官网、英文网站、手机端网站、网站推广等服务,满足客户于互联网时代的临澧网站设计、移动媒体设计的需求,帮助企业找到有效的互联网解决方案。努力成为您成熟可靠的网络建设合作伙伴!
主要解析了函数AssignTransactionId->GetNewTransactionId的实现逻辑。
在GetNewTransactionId函数中,检查是否可以安全的分配XID.
这可以防止由于XID wraparound而导致的灾难性数据丢失.
基本规则是:
如果超过了xidVacLimit,开始尝试强制autovacuum循环.
如果超过了xidWarnLimit,开始发出警告.
如果超过了xidStopLimit,拒绝执行事务,直至以单用户模式运行.
(这为DBA提供了一个逃生通道,使他们能够通过数据库早期的安全性检测,进入数据库进行维护)
ShmemVariableCache->xidVacLimit —> 200,000,561 —> 大于等于该值,触发自动vacuum
ShmemVariableCache->xidWarnLimit —> 2,136,484,208 —> 大于等于该值,系统报警
ShmemVariableCache->xidStopLimit —> 2,146,484,208 —> 大于等于该值,系统不允许执行事务,使用单用户模式处理
一、数据结构
TransactionState
事务状态结构体
/* * transaction states - transaction state from server perspective * 事务状态枚举 - 服务器视角的事务状态 */ typedef enum TransState { TRANS_DEFAULT, /* idle 空闲 */ TRANS_START, /* transaction starting 事务启动 */ TRANS_INPROGRESS, /* inside a valid transaction 进行中 */ TRANS_COMMIT, /* commit in progress 提交中 */ TRANS_ABORT, /* abort in progress 回滚中 */ TRANS_PREPARE /* prepare in progress 准备中 */ } TransState; /* * transaction block states - transaction state of client queries * 事务块状态 - 客户端查询的事务状态 * * Note: the subtransaction states are used only for non-topmost * transactions; the others appear only in the topmost transaction. * 注意:subtransaction只用于非顶层事务;其他字段用于顶层事务. */ typedef enum TBlockState { /* not-in-transaction-block states 未进入事务块状态 */ TBLOCK_DEFAULT, /* idle 空闲 */ TBLOCK_STARTED, /* running single-query transaction 单个查询事务 */ /* transaction block states 事务块状态 */ TBLOCK_BEGIN, /* starting transaction block 开始事务块 */ TBLOCK_INPROGRESS, /* live transaction 进行中 */ TBLOCK_IMPLICIT_INPROGRESS, /* live transaction after implicit BEGIN 隐式事务,进行中 */ TBLOCK_PARALLEL_INPROGRESS, /* live transaction inside parallel worker 并行worker中的事务,进行中 */ TBLOCK_END, /* COMMIT received 接收到COMMIT */ TBLOCK_ABORT, /* failed xact, awaiting ROLLBACK 失败,等待ROLLBACK */ TBLOCK_ABORT_END, /* failed xact, ROLLBACK received 失败,已接收ROLLBACK */ TBLOCK_ABORT_PENDING, /* live xact, ROLLBACK received 进行中,接收到ROLLBACK */ TBLOCK_PREPARE, /* live xact, PREPARE received 进行中,接收到PREPARE */ /* subtransaction states 子事务状态 */ TBLOCK_SUBBEGIN, /* starting a subtransaction 开启 */ TBLOCK_SUBINPROGRESS, /* live subtransaction 进行中 */ TBLOCK_SUBRELEASE, /* RELEASE received 接收到RELEASE */ TBLOCK_SUBCOMMIT, /* COMMIT received while TBLOCK_SUBINPROGRESS 进行中,接收到COMMIT */ TBLOCK_SUBABORT, /* failed subxact, awaiting ROLLBACK 失败,等待ROLLBACK */ TBLOCK_SUBABORT_END, /* failed subxact, ROLLBACK received 失败,已接收ROLLBACK */ TBLOCK_SUBABORT_PENDING, /* live subxact, ROLLBACK received 进行中,接收到ROLLBACK */ TBLOCK_SUBRESTART, /* live subxact, ROLLBACK TO received 进行中,接收到ROLLBACK TO */ TBLOCK_SUBABORT_RESTART /* failed subxact, ROLLBACK TO received 失败,已接收ROLLBACK TO */ } TBlockState; /* * transaction state structure * 事务状态结构体 */ typedef struct TransactionStateData { //事务ID TransactionId transactionId; /* my XID, or Invalid if none */ //子事务ID SubTransactionId subTransactionId; /* my subxact ID */ //保存点名称 char *name; /* savepoint name, if any */ //保存点级别 int savepointLevel; /* savepoint level */ //低级别的事务状态 TransState state; /* low-level state */ //高级别的事务状态 TBlockState blockState; /* high-level state */ //事务嵌套深度 int nestingLevel; /* transaction nesting depth */ //GUC上下文嵌套深度 int gucNestLevel; /* GUC context nesting depth */ //事务生命周期上下文 MemoryContext curTransactionContext; /* my xact-lifetime context */ //查询资源 ResourceOwner curTransactionOwner; /* my query resources */ //按XID顺序保存的已提交的子事务ID TransactionId *childXids; /* subcommitted child XIDs, in XID order */ //childXids数组大小 int nChildXids; /* # of subcommitted child XIDs */ //分配的childXids数组空间 int maxChildXids; /* allocated size of childXids[] */ //上一个CurrentUserId Oid prevUser; /* previous CurrentUserId setting */ //上一个SecurityRestrictionContext int prevSecContext; /* previous SecurityRestrictionContext */ //上一事务是否只读? bool prevXactReadOnly; /* entry-time xact r/o state */ //是否处于Recovery? bool startedInRecovery; /* did we start in recovery? */ //XID是否已保存在WAL Record中? bool didLogXid; /* has xid been included in WAL record? */ //Enter/ExitParallelMode计数器 int parallelModeLevel; /* Enter/ExitParallelMode counter */ //父事务状态 struct TransactionStateData *parent; /* back link to parent */ } TransactionStateData; //结构体指针 typedef TransactionStateData *TransactionState;
ShmemVariableCache
VariableCache是共享内存中的一种数据结构,用于跟踪OID和XID分配状态。
ShmemVariableCache—>共享内存中的指针
/* * VariableCache is a data structure in shared memory that is used to track * OID and XID assignment state. For largely historical reasons, there is * just one struct with different fields that are protected by different * LWLocks. * VariableCache是共享内存中的一种数据结构,用于跟踪OID和XID分配状态。 * 由于历史原因,这个结构体有不同的字段,由不同的LWLocks保护。 * * Note: xidWrapLimit and oldestXidDB are not "active" values, but are * used just to generate useful messages when xidWarnLimit or xidStopLimit * are exceeded. * 注意:xidWrapLimit和oldestXidDB是不"活跃"的值,在xidWarnLimit或xidStopLimit * 超出限制时用于产生有用的信息. */ typedef struct VariableCacheData { /* * These fields are protected by OidGenLock. * 这些域字段通过OidGenLock字段保护 */ //下一个待分配的OID Oid nextOid; /* next OID to assign */ //在必须执行XLOG work前可用OIDs uint32 oidCount; /* OIDs available before must do XLOG work */ /* * These fields are protected by XidGenLock. * 这些字段通过XidGenLock锁保护. */ //下一个待分配的事务ID TransactionId nextXid; /* next XID to assign */ //集群范围内最小datfrozenxid TransactionId oldestXid; /* cluster-wide minimum datfrozenxid */ //在该XID开始强制执行autovacuum TransactionId xidVacLimit; /* start forcing autovacuums here */ //在该XID开始提出警告 TransactionId xidWarnLimit; /* start complaining here */ //在该XID开外,拒绝生成下一个XID TransactionId xidStopLimit; /* refuse to advance nextXid beyond here */ //"世界末日"XID,需回卷 TransactionId xidWrapLimit; /* where the world ends */ //持有最小datfrozenxid的DB Oid oldestXidDB; /* database with minimum datfrozenxid */ /* * These fields are protected by CommitTsLock * 这些字段通过CommitTsLock锁保护 */ TransactionId oldestCommitTsXid; TransactionId newestCommitTsXid; /* * These fields are protected by ProcArrayLock. * 这些字段通过ProcArrayLock锁保护 */ TransactionId latestCompletedXid; /* newest XID that has committed or * aborted */ /* * These fields are protected by CLogTruncationLock * 这些字段通过CLogTruncationLock锁保护 */ //clog中最古老的XID TransactionId oldestClogXid; /* oldest it's safe to look up in clog */ } VariableCacheData; //结构体指针 typedef VariableCacheData *VariableCache; /* pointer to "variable cache" in shared memory (set up by shmem.c) */ //共享内存中的指针(通过shmem.c设置) VariableCache ShmemVariableCache = NULL;
二、源码解读
AssignTransactionId函数,给定的TransactionState分配一个新的持久化事务号XID,在此函数调用前,不会为事务分配XIDs.GetNewTransactionId是获取事务ID实际执行的地方,该函数从共享内存变量ShmemVariableCache中获取nextXid,+1后作为新的XID.
/* * Allocate the next XID for a new transaction or subtransaction. * 为新事务或新子事务分配XID * * The new XID is also stored into MyPgXact before returning. * 在返回前,XID会存储在全局变量MyPgXact中 * * Note: when this is called, we are actually already inside a valid * transaction, since XIDs are now not allocated until the transaction * does something. So it is safe to do a database lookup if we want to * issue a warning about XID wrap. * 注意:在该函数调用时,我们实际上已在一个有效的事务中,因为XIDs在事务不做些事情前不会分配. * 因此,如果我们想发出关于XID wrap回卷的警告,那么进行数据库查找是安全的。 */ TransactionId GetNewTransactionId(bool isSubXact) { TransactionId xid; /* * Workers synchronize transaction state at the beginning of each parallel * operation, so we can't account for new XIDs after that point. * 在每个并行操作前,Parallel Workers同步事务状态, * 因此我们不能在这时候请求XIDs */ if (IsInParallelMode()) elog(ERROR, "cannot assign TransactionIds during a parallel operation"); /* * During bootstrap initialization, we return the special bootstrap * transaction id. * 在宇宙初启时,返回特别的bootstrap事务ID */ if (IsBootstrapProcessingMode()) { Assert(!isSubXact); MyPgXact->xid = BootstrapTransactionId; return BootstrapTransactionId;//--> 1 } /* safety check, we should never get this far in a HS standby */ * 安全检查 if (RecoveryInProgress()) elog(ERROR, "cannot assign TransactionIds during recovery"); LWLockAcquire(XidGenLock, LW_EXCLUSIVE); //从共享内存中获取下一个XID xid = ShmemVariableCache->nextXid; /*---------- * Check to see if it's safe to assign another XID. This protects against * catastrophic data loss due to XID wraparound. The basic rules are: * 检查是否可以安全的分配另外一个XID. * 这可以防止由于XID wraparound而导致的灾难性数据丢失. * 基本规则是: * * If we're past xidVacLimit, start trying to force autovacuum cycles. * If we're past xidWarnLimit, start issuing warnings. * If we're past xidStopLimit, refuse to execute transactions, unless * we are running in single-user mode (which gives an escape hatch * to the DBA who somehow got past the earlier defenses). * 如果超过了xidVacLimit,开始尝试强制autovacuum循环. * 如果超过了xidWarnLimit,开始发出警告. * 如果超过了xidStopLimit,拒绝执行事务,直至以单用户模式运行. * (这为DBA提供了一个逃生通道,使他们能够通过数据库早期的安全性检测) * * Note that this coding also appears in GetNewMultiXactId. * 注意这部分代码在GetNewMultiXactId中也会出现. *---------- */ //TransactionIdFollowsOrEquals --> is id1 logically >= id2? if (TransactionIdFollowsOrEquals(xid, ShmemVariableCache->xidVacLimit)) { //xid >= ShmemVariableCache->xidVacLimit /* * For safety's sake, we release XidGenLock while sending signals, * warnings, etc. This is not so much because we care about * preserving concurrency in this situation, as to avoid any * possibility of deadlock while doing get_database_name(). First, * copy all the shared values we'll need in this path. * 为了安全起见,我们在发送信号、警告等时释放XidGenLock。 * 这并不是因为我们关心在这种情况下并发性, * 而是因为在执行get_database_name()时要避免出现死锁 */ //获取相关XID TransactionId xidWarnLimit = ShmemVariableCache->xidWarnLimit; TransactionId xidStopLimit = ShmemVariableCache->xidStopLimit; TransactionId xidWrapLimit = ShmemVariableCache->xidWrapLimit; Oid oldest_datoid = ShmemVariableCache->oldestXidDB; LWLockRelease(XidGenLock); /* * To avoid swamping the postmaster with signals, we issue the autovac * request only once per 64K transaction starts. This still gives * plenty of chances before we get into real trouble. * 为了避免信号淹没postmaster,我们每64K事务开始时只发出一次autovac请求。 * 在我们陷入真正的麻烦之前,这仍然给了我们很多解决问题的机会。 */ if (IsUnderPostmaster && (xid % 65536) == 0) //每隔64K发一次 SendPostmasterSignal(PMSIGNAL_START_AUTOVAC_LAUNCHER); if (IsUnderPostmaster && TransactionIdFollowsOrEquals(xid, xidStopLimit)) { //xid >= ShmemVariableCache->xidStopLimit char *oldest_datname = get_database_name(oldest_datoid); /* complain even if that DB has disappeared */ //就算DB已消失,也要不停的警告:( if (oldest_datname) ereport(ERROR, (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED), errmsg("database is not accepting commands to avoid wraparound data loss in database \"%s\"", oldest_datname), errhint("Stop the postmaster and vacuum that database in single-user mode.\n" "You might also need to commit or roll back old prepared transactions, or drop stale replication slots."))); else ereport(ERROR, (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED), errmsg("database is not accepting commands to avoid wraparound data loss in database with OID %u", oldest_datoid), errhint("Stop the postmaster and vacuum that database in single-user mode.\n" "You might also need to commit or roll back old prepared transactions, or drop stale replication slots."))); } else if (TransactionIdFollowsOrEquals(xid, xidWarnLimit)) { ////xid >= ShmemVariableCache->xidWarnLimit char *oldest_datname = get_database_name(oldest_datoid); /* complain even if that DB has disappeared */ if (oldest_datname) ereport(WARNING, (errmsg("database \"%s\" must be vacuumed within %u transactions", oldest_datname, xidWrapLimit - xid), errhint("To avoid a database shutdown, execute a database-wide VACUUM in that database.\n" "You might also need to commit or roll back old prepared transactions, or drop stale replication slots."))); else ereport(WARNING, (errmsg("database with OID %u must be vacuumed within %u transactions", oldest_datoid, xidWrapLimit - xid), errhint("To avoid a database shutdown, execute a database-wide VACUUM in that database.\n" "You might also need to commit or roll back old prepared transactions, or drop stale replication slots."))); } /* Re-acquire lock and start over */ //重新获取锁并启动 LWLockAcquire(XidGenLock, LW_EXCLUSIVE); xid = ShmemVariableCache->nextXid; } /* * If we are allocating the first XID of a new page of the commit log, * zero out that commit-log page before returning. We must do this while * holding XidGenLock, else another xact could acquire and commit a later * XID before we zero the page. Fortunately, a page of the commit log * holds 32K or more transactions, so we don't have to do this very often. * 如果在clog的新page中分配第一个XID,返回前初始化clog page. * 必须在持有XidGenLock锁时执行这个操作,否则的话, * 其他事务可能会请求该锁并在初始化page前提交了一个新事务. * 幸运的是,提交日志的一个页面包含32K或更多的事务,所以我们不需要经常这样做。 * * Extend pg_subtrans and pg_commit_ts too. * 同时扩展pg_subtrans和pg_commit_ts */ ExtendCLOG(xid); ExtendCommitTs(xid); ExtendSUBTRANS(xid); /* * Now advance the nextXid counter. This must not happen until after we * have successfully completed ExtendCLOG() --- if that routine fails, we * want the next incoming transaction to try it again. We cannot assign * more XIDs until there is CLOG space for them. * 现在可以更新nextXid计数器了. * 这必须在我们成功完成ExtendCLOG()之后才能执行——如果该例程失败, * 我们希望下一个进入的事务再次尝试。 * 不能分配更多的xid,除非有空闲的CLOG空间。 */ //ShmemVariableCache->nextXid ++ //if ((ShmemVariableCache->nextXid) < FirstNormalTransactionId) // (ShmemVariableCache->nextXid) = FirstNormalTransactionId; TransactionIdAdvance(ShmemVariableCache->nextXid); /* * We must store the new XID into the shared ProcArray before releasing * XidGenLock. This ensures that every active XID older than * latestCompletedXid is present in the ProcArray, which is essential for * correct OldestXmin tracking; see src/backend/access/transam/README. * 在释放XidGenLock前,存储新的XID到共享数据结构ProcArray中. * 这可以确保每一个活动的比latestCompletedXid旧的XID都会出现在ProcArray中, * 这样可以减少OldestXmin的跟踪,请查看src/backend/access/transam/README. * * Note that readers of PGXACT xid fields should be careful to fetch the * value only once, rather than assume they can read a value multiple * times and get the same answer each time. Note we are assuming that * TransactionId and int fetch/store are atomic. * 要注意的是读取PGXACT.xid字段时小心只提取一次, * 而不是假定可以多次读取该值而认为每次返回的值都一样. * 同时我们假定TransactionId和int 提取/写入是原子的. * * The same comments apply to the subxact xid count and overflow fields. * 对于subxact xid计数器和溢出字段,参见上述注释. * * Use of a write barrier prevents dangerous code rearrangement in this * function; other backends could otherwise e.g. be examining my subxids * info concurrently, and we don't want them to see an invalid * intermediate state, such as an incremented nxids before the array entry * is filled. * 在该函数中,进入堡垒进行写可以防止危险的事情出现. * 否则其他后台进程可能会比如同步检查本进程的subxids信息,但我们不希望它们看到无效的中间状态, * 例如,在数组条目被填充之前增加nxid。 * * Other processes that read nxids should do so before reading xids * elements with a pg_read_barrier() in between, so that they can be sure * not to read an uninitialized array element; see * src/backend/storage/lmgr/README.barrier. * 其他读取nxids的进程应在使用pg_read_barrier()函数读取xids条目前执行相关操作, * 这样可以确保它们不会读取到未经初始化的数组条目,查看src/backend/storage/lmgr/README.barrier说明. * * If there's no room to fit a subtransaction XID into PGPROC, set the * cache-overflowed flag instead. This forces readers to look in * pg_subtrans to map subtransaction XIDs up to top-level XIDs. There is a * race-condition window, in that the new XID will not appear as running * until its parent link has been placed into pg_subtrans. However, that * will happen before anyone could possibly have a reason to inquire about * the status of the XID, so it seems OK. (Snapshots taken during this * window *will* include the parent XID, so they will deliver the correct * answer later on when someone does have a reason to inquire.) * 如果没有空间将子事务XID放入PGPROC中,则设置cache-overflow标志。 * 这可以强制readers查看pg_subtrans以将子事务xid映射到顶层xid。 * 有一个race-condition窗口,在它的父链接被放入pg_subtrans之前,新的XID不会显示为正在运行. * 然而,在都有可能有理由查询XID的状态之前,这种情况就会发生,所以看起来是可以的。 * (在这个窗口中拍摄的快照*将*包含父XID,因此当稍后有进程查询时,它们将提供正确的答案。) */ if (!isSubXact) MyPgXact->xid = xid; /* LWLockRelease acts as barrier */ else { int nxids = MyPgXact->nxids; if (nxids < PGPROC_MAX_CACHED_SUBXIDS) { MyProc->subxids.xids[nxids] = xid; pg_write_barrier(); MyPgXact->nxids = nxids + 1; } else MyPgXact->overflowed = true; } //释放锁 LWLockRelease(XidGenLock); return xid; }
三、跟踪分析
执行txid_current,触发函数调用
14:26:26 (xdb@[local]:5432)testdb=# begin; BEGIN 14:26:50 (xdb@[local]:5432)testdb=#* select txid_current_if_assigned(); txid_current_if_assigned -------------------------- (1 row) 14:26:55 (xdb@[local]:5432)testdb=#* select txid_current();
启动gdb,设置断点
(gdb) b GetNewTransactionId Breakpoint 6 at 0x545e80: file varsup.c, line 56. (gdb) c Continuing. Breakpoint 6, GetNewTransactionId (isSubXact=false) at varsup.c:56 56 if (IsInParallelMode()) (gdb)
查看调用栈
(gdb) bt #0 GetNewTransactionId (isSubXact=false) at varsup.c:56 #1 0x0000000000546bd9 in AssignTransactionId (s=0xf9c720) at xact.c:557 #2 0x000000000054693d in GetTopTransactionId () at xact.c:392 #3 0x00000000009fe1f3 in txid_current (fcinfo=0x254c7e0) at txid.c:443 #4 0x00000000006cfebd in ExecInterpExpr (state=0x254c6f8, econtext=0x254c3e8, isnull=0x7ffe3d4a31f7) at execExprInterp.c:654 #5 0x00000000006d1ac6 in ExecInterpExprStillValid (state=0x254c6f8, econtext=0x254c3e8, isNull=0x7ffe3d4a31f7) at execExprInterp.c:1786 #6 0x00000000007140dd in ExecEvalExprSwitchContext (state=0x254c6f8, econtext=0x254c3e8, isNull=0x7ffe3d4a31f7) at ../../../src/include/executor/executor.h:303 #7 0x000000000071414b in ExecProject (projInfo=0x254c6f0) at ../../../src/include/executor/executor.h:337 #8 0x0000000000714323 in ExecResult (pstate=0x254c2d0) at nodeResult.c:136 #9 0x00000000006e4c30 in ExecProcNodeFirst (node=0x254c2d0) at execProcnode.c:445 #10 0x00000000006d9974 in ExecProcNode (node=0x254c2d0) at ../../../src/include/executor/executor.h:237 #11 0x00000000006dc22d in ExecutePlan (estate=0x254c0b8, planstate=0x254c2d0, use_parallel_mode=false, operation=CMD_SELECT, sendTuples=true, numberTuples=0, direction=ForwardScanDirection, dest=0x24ccf10, execute_once=true) at execMain.c:1723 #12 0x00000000006d9f5c in standard_ExecutorRun (queryDesc=0x256b8e8, direction=ForwardScanDirection, count=0, execute_once=true) at execMain.c:364 #13 0x00000000006d9d7f in ExecutorRun (queryDesc=0x256b8e8, direction=ForwardScanDirection, count=0, execute_once=true) at execMain.c:307 #14 0x00000000008ccf5a in PortalRunSelect (portal=0x250c860, forward=true, count=0, dest=0x24ccf10) at pquery.c:932 #15 0x00000000008ccbf3 in PortalRun (portal=0x250c860, count=9223372036854775807, isTopLevel=true, run_once=true, dest=0x24ccf10, altdest=0x24ccf10, completionTag=0x7ffe3d4a3570 "") at pquery.c:773 #16 0x00000000008c6b1e in exec_simple_query (query_string=0x24a6ec8 "select txid_current();") at postgres.c:1145 #17 0x00000000008cae70 in PostgresMain (argc=1, argv=0x24d2dc8, dbname=0x24d2c30 "testdb", username=0x24a3ba8 "xdb") at postgres.c:4182 ---Type to continue, or q to quit--- #18 0x000000000082642b in BackendRun (port=0x24c8c00) at postmaster.c:4361 #19 0x0000000000825b8f in BackendStartup (port=0x24c8c00) at postmaster.c:4033 #20 0x0000000000821f1c in ServerLoop () at postmaster.c:1706 #21 0x00000000008217b4 in PostmasterMain (argc=1, argv=0x24a1b60) at postmaster.c:1379 #22 0x00000000007488ef in main (argc=1, argv=0x24a1b60) at main.c:228 (gdb)
获取XidGenLock锁
(gdb) n 63 if (IsBootstrapProcessingMode()) (gdb) 71 if (RecoveryInProgress()) (gdb) 74 LWLockAcquire(XidGenLock, LW_EXCLUSIVE); (gdb)
获取共享内存变量ShmemVariableCache->nextXid —> 2409
(gdb) 76 xid = ShmemVariableCache->nextXid; (gdb) 91 if (TransactionIdFollowsOrEquals(xid, ShmemVariableCache->xidVacLimit)) (gdb) p *ShmemVariableCache $16 = {nextOid = 42628, oidCount = 8191, nextXid = 2409, oldestXid = 561, xidVacLimit = 200000561, xidWarnLimit = 2136484208, xidStopLimit = 2146484208, xidWrapLimit = 2147484208, oldestXidDB = 16400, oldestCommitTsXid = 0, newestCommitTsXid = 0, latestCompletedXid = 2408, oldestClogXid = 561} (gdb)
扩展clog
(gdb) n 171 ExtendCLOG(xid); (gdb) 172 ExtendCommitTs(xid); (gdb) 173 ExtendSUBTRANS(xid); (gdb) 181 TransactionIdAdvance(ShmemVariableCache->nextXid); (gdb)
ShmemVariableCache->nextXid++ —> 2410
(gdb) p ShmemVariableCache->nextXid $17 = 2410
获取进程和事务信息
(gdb) n 223 volatile PGXACT *mypgxact = MyPgXact; (gdb) 225 if (!isSubXact) (gdb) 226 mypgxact->xid = xid; (gdb)
释放锁XidGenLock
(gdb) 241 LWLockRelease(XidGenLock); (gdb) p *ShmemVariableCache $18 = {nextOid = 42628, oidCount = 8191, nextXid = 2410, oldestXid = 561, xidVacLimit = 200000561, xidWarnLimit = 2136484208, xidStopLimit = 2146484208, xidWrapLimit = 2147484208, oldestXidDB = 16400, oldestCommitTsXid = 0, newestCommitTsXid = 0, latestCompletedXid = 2408, oldestClogXid = 561}
返回xid(2409),完成调用.
(gdb) n 243 return xid; (gdb) 244 } (gdb) AssignTransactionId (s=0xf9c720) at xact.c:558 558 if (!isSubXact) (gdb)
以上是“PostgreSQL如何获取事务号XID”这篇文章的所有内容,感谢各位的阅读!相信大家都有了一定的了解,希望分享的内容对大家有所帮助,如果还想学习更多知识,欢迎关注创新互联行业资讯频道!
网页标题:PostgreSQL如何获取事务号XID
文章网址:http://azwzsj.com/article/jhcsss.html