本节介绍了PostgreSQL判断元组可见性的实现逻辑,重点解析了HeapTupleSatisfiesMVCC函数的处理过程。
一、数据结构
宏定义
//事务ID,无符号32位整型
typedef uint32 TransactionId;
#define InvalidTransactionId ((TransactionId) 0)
#define BootstrapTransactionId ((TransactionId) 1)
#define FrozenTransactionId ((TransactionId) 2)
#define FirstNormalTransactionId ((TransactionId) 3)
#define MaxTransactionId ((TransactionId) 0xFFFFFFFF)
#define TransactionIdIsValid(xid) ((xid) != InvalidTransactionId)
#define TransactionIdIsNormal(xid) ((xid) >= FirstNormalTransactionId)
#define TransactionIdEquals(id1, id2) ((id1) == (id2))
#define TransactionIdStore(xid, dest) (*(dest) = (xid))
#define StoreInvalidTransactionId(dest) (*(dest) = InvalidTransactionId)
#define TransactionIdAdvance(dest) \
do { \
(dest)++; \
if ((dest) < FirstNormalTransactionId) \
(dest) = FirstNormalTransactionId; \
} while(0)
#define TransactionIdRetreat(dest) \
do { \
(dest)--; \
} while ((dest) < FirstNormalTransactionId)
//比较两个已知是常规事务的XIDs;宏定义是为了性能考虑.
#define NormalTransactionIdPrecedes(id1, id2) \
(AssertMacro(TransactionIdIsNormal(id1) && TransactionIdIsNormal(id2)), \
(int32) ((id1) - (id2)) < 0)
#define NormalTransactionIdFollows(id1, id2) \
(AssertMacro(TransactionIdIsNormal(id1) && TransactionIdIsNormal(id2)), \
(int32) ((id1) - (id2)) > 0)
#define HeapTupleHeaderGetRawXmin(tup) \
( \
(tup)->t_choice.t_heap.t_xmin \
)
#define HeapTupleHeaderXminCommitted(tup) \
( \
((tup)->t_infomask & HEAP_XMIN_COMMITTED) != 0 \
)
#define HeapTupleHeaderGetUpdateXid(tup) \
( \
(!((tup)->t_infomask & HEAP_XMAX_INVALID) && \
((tup)->t_infomask & HEAP_XMAX_IS_MULTI) && \
!((tup)->t_infomask & HEAP_XMAX_LOCK_ONLY)) ? \
HeapTupleGetUpdateXid(tup) \
: \
HeapTupleHeaderGetRawXmax(tup) \
)
#define HeapTupleHeaderGetRawXmax(tup) \
( \
(tup)->t_choice.t_heap.t_xmax \
)
#define HEAP_XMAX_IS_LOCKED_ONLY(infomask) \
(((infomask) & HEAP_XMAX_LOCK_ONLY) || \
(((infomask) & (HEAP_XMAX_IS_MULTI | HEAP_LOCK_MASK)) == HEAP_XMAX_EXCL_LOCK))
二、源码解读
全局变量snapshot->satisfies函数在初始化该全局变量已设置为HeapTupleSatisfiesMVCC.
static SnapshotData CurrentSnapshotData = {HeapTupleSatisfiesMVCC};
HeapTupleSatisfiesMVCC — 如Tuple对于给定的MVCC快照可见,则返回T
bool
HeapTupleSatisfiesMVCC(HeapTuple htup, Snapshot snapshot,
Buffer buffer)
{
HeapTupleHeader tuple = htup->t_data;//Tuple
Assert(ItemPointerIsValid(&htup->t_self));
Assert(htup->t_tableOid != InvalidOid);
if (!HeapTupleHeaderXminCommitted(tuple))
{
//A.xmin事务未提交(HEAP_XMIN_COMMITTED标记未设置)
if (HeapTupleHeaderXminInvalid(tuple))
//xmin = 0,此tuple不可见
return false;
//用于9.0前二进制可执行文件的升级,HEAP_MOVED_OFF & HEAP_MOVED_IN已不再使用
if (tuple->t_infomask & HEAP_MOVED_OFF)
{
TransactionId xvac = HeapTupleHeaderGetXvac(tuple);
if (TransactionIdIsCurrentTransactionId(xvac))
return false;
if (!XidInMVCCSnapshot(xvac, snapshot))
{
if (TransactionIdDidCommit(xvac))
{
SetHintBits(tuple, buffer, HEAP_XMIN_INVALID,
InvalidTransactionId);
return false;
}
SetHintBits(tuple, buffer, HEAP_XMIN_COMMITTED,
InvalidTransactionId);
}
}
//同上
else if (tuple->t_infomask & HEAP_MOVED_IN)
{
TransactionId xvac = HeapTupleHeaderGetXvac(tuple);
if (!TransactionIdIsCurrentTransactionId(xvac))
{
if (XidInMVCCSnapshot(xvac, snapshot))
return false;
if (TransactionIdDidCommit(xvac))
SetHintBits(tuple, buffer, HEAP_XMIN_COMMITTED,
InvalidTransactionId);
else
{
SetHintBits(tuple, buffer, HEAP_XMIN_INVALID,
InvalidTransactionId);
return false;
}
}
}
else if (TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetRawXmin(tuple)))
{
//A.xmin事务未提交
//A1.xmin对应的事务是当前事务
if (HeapTupleHeaderGetCmin(tuple) >= snapshot->curcid)
//A1-1.tuple.cmin >= 快照的cid,说明插入在扫描开始前,返回F
return false;
if (tuple->t_infomask & HEAP_XMAX_INVALID)
//A1-2.xmax无效,返回T
return true;
if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask))
//A1-3.XMAX仅仅只是锁定而已,返回T
return true;
if (tuple->t_infomask & HEAP_XMAX_IS_MULTI)
{
//xmax is a MultiXactId
TransactionId xmax;
//获取更新的Xid
xmax = HeapTupleGetUpdateXid(tuple);
//验证xmax是有效的
Assert(TransactionIdIsValid(xmax));
//执行更新的子事务必须已回滚(why?因为前提是xmin事务是未提交的,因此xmax必须已回滚)
if (!TransactionIdIsCurrentTransactionId(xmax))
//正在执行删除的事务不是本事务,返回T
return true;
else if (HeapTupleHeaderGetCmax(tuple) >= snapshot->curcid)
//本事务执行删除:删除命令在快照之后,返回T
return true;
else
//本事务执行删除:删除命令在快照之前,返回F
return false;
}
if (!TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetRawXmax(tuple)))
{
//A1-4.tuple.xmax对应的事务不是本事务
//删除子事务已终止(why?因为前提是xmin事务是未提交的,因此xmax必须已回滚)
SetHintBits(tuple, buffer, HEAP_XMAX_INVALID,
InvalidTransactionId);
return true;
}
if (HeapTupleHeaderGetCmax(tuple) >= snapshot->curcid)
//A1-5.删除在快照之后,返回T
return true;
else
//A1-6.删在快照之前,返回F
return false;
}
else if (XidInMVCCSnapshot(HeapTupleHeaderGetRawXmin(tuple), snapshot))
//A.xmin事务未提交;
//A2.xmin在当前快照中(非本事务) --> 不可见
return false;
else if (TransactionIdDidCommit(HeapTupleHeaderGetRawXmin(tuple)))
//A.xmin事务未提交;
//A3.查询事务日志clog,Xmin事务已提交,则设置标志位,可见判断逻辑在后续执行
SetHintBits(tuple, buffer, HEAP_XMIN_COMMITTED,
HeapTupleHeaderGetRawXmin(tuple));
else
{
//A4.以上各种情况都不符合,事务回滚或者在执行事务过程中crash了
//设置标志位,返回F
SetHintBits(tuple, buffer, HEAP_XMIN_INVALID,
InvalidTransactionId);
return false;
}
}
else
{
//B.xmin事务已提交,但可能不属于该快照
if (!HeapTupleHeaderXminFrozen(tuple) &&
XidInMVCCSnapshot(HeapTupleHeaderGetRawXmin(tuple), snapshot))
return false;
}
//C.Xmin事务已提交
if (tuple->t_infomask & HEAP_XMAX_INVALID)
//C1.xmax无效(HEAP_XMAX_INVALID),则返回T
return true;
if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask))
//C2.xmax仅仅只是lock而已,返回T
return true;
if (tuple->t_infomask & HEAP_XMAX_IS_MULTI)
{
//xmax is a MultiXactId
TransactionId xmax;
//再次检查
Assert(!HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask));
//获取xmax
xmax = HeapTupleGetUpdateXid(tuple);
Assert(TransactionIdIsValid(xmax));
if (TransactionIdIsCurrentTransactionId(xmax))
{
//xmax为当前事务id
if (HeapTupleHeaderGetCmax(tuple) >= snapshot->curcid)
//快照之后执行删除,返回T
return true;
else
//快照之前执行删除,返回F
return false;
}
if (XidInMVCCSnapshot(xmax, snapshot))
//xmax仍在进行中,返回T
return true;
if (TransactionIdDidCommit(xmax))
//xmax事务通过查询clog日志,已确认提交,则返回F
return false;
//xmax回滚或者执行期间crash,返回T
return true;
}
if (!(tuple->t_infomask & HEAP_XMAX_COMMITTED))
{
//C3.xmax未提交
if (TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetRawXmax(tuple)))
{
//C3-1:本事务
if (HeapTupleHeaderGetCmax(tuple) >= snapshot->curcid)
return true;
else
return false;
}
if (XidInMVCCSnapshot(HeapTupleHeaderGetRawXmax(tuple), snapshot))
//C3-2:非本事务
//xmax事务仍在进行中
return true;
if (!TransactionIdDidCommit(HeapTupleHeaderGetRawXmax(tuple)))
{
//C3-3查询clog日志,xmax事务未提交(回滚或crash),设置标记
SetHintBits(tuple, buffer, HEAP_XMAX_INVALID,
InvalidTransactionId);
//可见
return true;
}
//以上判断均不成立,则可认为事务已提交,设置标记
SetHintBits(tuple, buffer, HEAP_XMAX_COMMITTED,
HeapTupleHeaderGetRawXmax(tuple));
}
else
{
//C4.xmax已提交,但快照指示该事务仍在进行中
if (XidInMVCCSnapshot(HeapTupleHeaderGetRawXmax(tuple), snapshot))
//仍在处理中,返回T
return true;
}
//C5.xmax已提交,返回F
return false;
}
三、跟踪分析
创建数据表,插入数据,删除其中一行数据,提交,更新其中一条数据,不提交,执行查询.
session 1
11:55:00 (xdb@[local]:5432)testdb=# drop table t_mvcc2;
ERROR: table "t_mvcc2" does not exist
11:55:01 (xdb@[local]:5432)testdb=# create table t_mvcc2(c1 int not null,c2 varchar(40),c3 varchar(40));
CREATE TABLE
11:55:02 (xdb@[local]:5432)testdb=#
11:55:02 (xdb@[local]:5432)testdb=# insert into t_mvcc2(c1,c2,c3) values(1,'C2-1','C3-1');
INSERT 0 1
11:55:02 (xdb@[local]:5432)testdb=# insert into t_mvcc2(c1,c2,c3) values(2,'C2-2','C3-2');
INSERT 0 1
11:55:03 (xdb@[local]:5432)testdb=#
11:55:38 (xdb@[local]:5432)testdb=# begin;
BEGIN
11:55:38 (xdb@[local]:5432)testdb=#*
11:55:38 (xdb@[local]:5432)testdb=#* delete from t_mvcc2 where c1 = 1;
DELETE 1
11:55:38 (xdb@[local]:5432)testdb=#*
11:55:38 (xdb@[local]:5432)testdb=#* commit;
COMMIT
11:55:38 (xdb@[local]:5432)testdb=#
11:55:38 (xdb@[local]:5432)testdb=# begin;
BEGIN
11:55:38 (xdb@[local]:5432)testdb=#*
11:55:38 (xdb@[local]:5432)testdb=#* update t_mvcc2 set c2 = 'C2#'||substr(c2,4,40) where c1 = 2;
UPDATE 1
11:55:38 (xdb@[local]:5432)testdb=#*
12:19:17 (xdb@[local]:5432)testdb=#* select txid_current();
txid_current
--------------
2363
(1 row)
另外启动session 2,查询数据表t_mvcc2
11:58:45 (xdb@[local]:5432)testdb=# select lp,lp_off,lp_flags,t_xmin,t_xmax,t_field3 as t_cid,t_ctid,t_infomask2,t_infomask from heap_page_items(get_raw_page('t_mvcc2',0));
lp | lp_off | lp_flags | t_xmin | t_xmax | t_cid | t_ctid | t_infomask2 | t_infomask
----+--------+----------+--------+--------+-------+--------+-------------+------------
1 | 8152 | 1 | 2360 | 2362 | 0 | (0,1) | 8195 | 1282
2 | 8112 | 1 | 2361 | 2363 | 0 | (0,3) | 16387 | 258
3 | 8072 | 1 | 2363 | 0 | 0 | (0,3) | 32771 | 10242
(3 rows)
11:59:38 (xdb@[local]:5432)testdb=# select * from t_mvcc2;
启动gdb,设置断点
(gdb) b HeapTupleSatisfiesMVCC
Breakpoint 1 at 0xa93125: file tqual.c, line 966.
(gdb) c
Continuing.
Breakpoint 1, HeapTupleSatisfiesMVCC (htup=0x1985d18, snapshot=0x1a06358, buffer=69) at tqual.c:966
966 HeapTupleHeader tuple = htup->t_data;
(gdb)
查看调用栈
(gdb) bt
#0 HeapTupleSatisfiesMVCC (htup=0x1985d18, snapshot=0x1a06358, buffer=69) at tqual.c:966
#1 0x00000000004de959 in heap_hot_search_buffer (tid=0x1985d1c, relation=0x7f002f73e2b0, buffer=69, snapshot=0x1a06358,
heapTuple=0x1985d18, all_dead=0x7ffc56ed8862, first_call=true) at heapam.c:2127
#2 0x00000000004fd12e in index_fetch_heap (scan=0x1985cb8) at indexam.c:608
#3 0x00000000004fd35d in index_getnext (scan=0x1985cb8, direction=ForwardScanDirection) at indexam.c:691
#4 0x00000000004fb910 in systable_getnext (sysscan=0x19868f8) at genam.c:425
#5 0x0000000000a1cb63 in SearchCatCacheMiss (cache=0x19de480, nkeys=1, hashValue=1281076841, hashIndex=105, v1=42610,
v2=0, v3=0, v4=0) at catcache.c:1386
#6 0x0000000000a1ca10 in SearchCatCacheInternal (cache=0x19de480, nkeys=1, v1=42610, v2=0, v3=0, v4=0) at catcache.c:1317
#7 0x0000000000a1c6fe in SearchCatCache1 (cache=0x19de480, v1=42610) at catcache.c:1185
#8 0x0000000000a37543 in SearchSysCache1 (cacheId=50, key1=42610) at syscache.c:1119
#9 0x0000000000a6ae60 in check_enable_rls (relid=42610, checkAsUser=0, noError=false) at rls.c:66
#10 0x000000000087b286 in get_row_security_policies (root=0x1985dd0, rte=0x1985ee8, rt_index=1,
securityQuals=0x7ffc56ed8cc0, withCheckOptions=0x7ffc56ed8cb8, hasRowSecurity=0x7ffc56ed8cb7,
hasSubLinks=0x7ffc56ed8cb6) at rowsecurity.c:133
#11 0x0000000000875e1c in fireRIRrules (parsetree=0x1985dd0, activeRIRs=0x0) at rewriteHandler.c:1904
#12 0x0000000000878b23 in QueryRewrite (parsetree=0x1985dd0) at rewriteHandler.c:3712
#13 0x00000000008c64d7 in pg_rewrite_query (query=0x1985dd0) at postgres.c:782
#14 0x00000000008c634e in pg_analyze_and_rewrite (parsetree=0x1985c20, query_string=0x1984ec8 "select * from t_mvcc2;",
paramTypes=0x0, numParams=0, queryEnv=0x0) at postgres.c:698
#15 0x00000000008c6993 in exec_simple_query (query_string=0x1984ec8 "select * from t_mvcc2;") at postgres.c:1070
#16 0x00000000008cae70 in PostgresMain (argc=1, argv=0x19b0dc8, dbname=0x19b0c30 "testdb", username=0x1981ba8 "xdb")
at postgres.c:4182
#17 0x000000000082642b in BackendRun (port=0x19a6c00) at postmaster.c:4361
#18 0x0000000000825b8f in BackendStartup (port=0x19a6c00) at postmaster.c:4033
#19 0x0000000000821f1c in ServerLoop () at postmaster.c:1706
#20 0x00000000008217b4 in PostmasterMain (argc=1, argv=0x197fb60) at postmaster.c:1379
---Type <return> to continue, or q <return> to quit---
#21 0x00000000007488ef in main (argc=1, argv=0x197fb60) at main.c:228
(gdb)
第一个Tuple
输入参数,主要是tuple/snapshot/buffer
(gdb) p *snapshot --> 快照信息
$3 = {satisfies = 0xa9310d <HeapTupleSatisfiesMVCC>, xmin = 2363, xmax = 2363, xip = 0x0, xcnt = 0, subxip = 0x0,
subxcnt = 0, suboverflowed = false, takenDuringRecovery = false, copied = true, curcid = 0, speculativeToken = 0,
active_count = 0, regd_count = 1, ph_node = {first_child = 0x0, next_sibling = 0x0,
prev_or_parent = 0xf9bfa0 <CatalogSnapshotData+64>}, whenTaken = 0, lsn = 0}
获取tuple
t_infomask = 2313,十六进制为0x0909,即HEAP_XMAX_INVALID | HEAP_XMIN_COMMITTED | HEAP_HASOID | HEAP_HASNULL
(gdb) n
968 Assert(ItemPointerIsValid(&htup->t_self));
(gdb) p tuple
$1 = (HeapTupleHeader) 0x7f0002da7a60
(gdb) p *tuple
$2 = {t_choice = {t_heap = {t_xmin = 2359, t_xmax = 0, t_field3 = {t_cid = 0, t_xvac = 0}}, t_datum = {datum_len_ = 2359,
datum_typmod = 0, datum_typeid = 0}}, t_ctid = {ip_blkid = {bi_hi = 0, bi_lo = 12}, ip_posid = 6}, t_infomask2 = 33,
t_infomask = 2313, t_hoff = 32 ' ', t_bits = 0x7f0002da7a77 "\377\377\377?"}
(gdb)
判断tuple.xmin是否已提交,按上一步的t_infomask标记,该事务已提交,进入相应逻辑
(gdb) n
969 Assert(htup->t_tableOid != InvalidOid);
(gdb)
971 if (!HeapTupleHeaderXminCommitted(tuple))
(gdb)
判断:B.xmin事务已提交,但可能不属于该快照
不符合此条件,继续执行
(gdb) n
1074 XidInMVCCSnapshot(HeapTupleHeaderGetRawXmin(tuple), snapshot))
(gdb)
1073 if (!HeapTupleHeaderXminFrozen(tuple) &&
(gdb)
1080 if (tuple->t_infomask & HEAP_XMAX_INVALID)
(gdb)
判断是否HEAP_XMAX_INVALID,按t_infomask标记,符合条件,返回T
(gdb)
1080 if (tuple->t_infomask & HEAP_XMAX_INVALID)
(gdb) n
1081 return true;
(gdb)
1148 }
(gdb)
第二个Tuple
接下来是第二个Tuple
(gdb) c
Continuing.
Breakpoint 1, HeapTupleSatisfiesMVCC (htup=0x1985d18, snapshot=0x1a06358, buffer=155) at tqual.c:966
966 HeapTupleHeader tuple = htup->t_data;
(gdb)
获取tuple并查看
t_infomask = 2313,十六进制为0x0909,即HEAP_XMAX_INVALID | HEAP_XMIN_COMMITTED | HEAP_HASOID | HEAP_HASNULL
(gdb) p *htup
$4 = {t_len = 172, t_self = {ip_blkid = {bi_hi = 0, bi_lo = 1}, ip_posid = 40}, t_tableOid = 1247, t_data = 0x7f0002e52800}
(gdb) n
968 Assert(ItemPointerIsValid(&htup->t_self));
(gdb) p tuple
$5 = (HeapTupleHeader) 0x7f0002e52800
(gdb) p *tuple
$6 = {t_choice = {t_heap = {t_xmin = 1, t_xmax = 0, t_field3 = {t_cid = 0, t_xvac = 0}}, t_datum = {datum_len_ = 1,
datum_typmod = 0, datum_typeid = 0}}, t_ctid = {ip_blkid = {bi_hi = 0, bi_lo = 1}, ip_posid = 40}, t_infomask2 = 30,
t_infomask = 2313, t_hoff = 32 ' ', t_bits = 0x7f0002e52817 "\377\377\377\a"}
(gdb)
与第一个Tuple类似,下面查看第三个Tupel
第三个Tuple
获取tuple并查看
t_infomask = 10505,十六进制值为0x2909,即HEAP_UPDATED | HEAP_XMAX_INVALID | HEAP_XMIN_COMMITTED | HEAP_HASOID | HEAP_HASNULL
(gdb) c
Continuing.
Breakpoint 1, HeapTupleSatisfiesMVCC (htup=0x1985d18, snapshot=0xf9bf60 <CatalogSnapshotData>, buffer=4) at tqual.c:966
966 HeapTupleHeader tuple = htup->t_data;
(gdb) n
968 Assert(ItemPointerIsValid(&htup->t_self));
(gdb)
969 Assert(htup->t_tableOid != InvalidOid);
(gdb) p *tuple
$7 = {t_choice = {t_heap = {t_xmin = 2117, t_xmax = 0, t_field3 = {t_cid = 7, t_xvac = 7}}, t_datum = {datum_len_ = 2117,
datum_typmod = 0, datum_typeid = 7}}, t_ctid = {ip_blkid = {bi_hi = 0, bi_lo = 1}, ip_posid = 58}, t_infomask2 = 33,
t_infomask = 10505, t_hoff = 32 ' ', t_bits = 0x7f0002d25aa7 "\377\377\377?"}
(gdb)
第三个Tuple,类似于第一/二个Tuple,仍返回T
(gdb) n
971 if (!HeapTupleHeaderXminCommitted(tuple))
(gdb)
1073 if (!HeapTupleHeaderXminFrozen(tuple) &&
(gdb)
1074 XidInMVCCSnapshot(HeapTupleHeaderGetRawXmin(tuple), snapshot))
(gdb)
1073 if (!HeapTupleHeaderXminFrozen(tuple) &&
(gdb)
1080 if (tuple->t_infomask & HEAP_XMAX_INVALID)
(gdb)
1081 return true;
(gdb)
1148 }
第四/五/六个Tuple
均有HEAP_XMAX_INVALID标志,不作展开
(gdb) c
Continuing.
Breakpoint 1, HeapTupleSatisfiesMVCC (htup=0x1985d18, snapshot=0x1a06358, buffer=44) at tqual.c:966
966 HeapTupleHeader tuple = htup->t_data;
(gdb) n
968 Assert(ItemPointerIsValid(&htup->t_self));
(gdb)
969 Assert(htup->t_tableOid != InvalidOid);
(gdb) p *tuple
$8 = {t_choice = {t_heap = {t_xmin = 1, t_xmax = 0, t_field3 = {t_cid = 75, t_xvac = 75}}, t_datum = {datum_len_ = 1,
datum_typmod = 0, datum_typeid = 75}}, t_ctid = {ip_blkid = {bi_hi = 0, bi_lo = 21}, ip_posid = 1}, t_infomask2 = 24,
t_infomask = 2305, t_hoff = 32 ' ', t_bits = 0x7f0002d76307 "\377\377\017"}
(gdb)
...
(gdb) p *tuple
$9 = {t_choice = {t_heap = {t_xmin = 1, t_xmax = 0, t_field3 = {t_cid = 75, t_xvac = 75}}, t_datum = {datum_len_ = 1,
datum_typmod = 0, datum_typeid = 75}}, t_ctid = {ip_blkid = {bi_hi = 0, bi_lo = 1}, ip_posid = 19}, t_infomask2 = 20,
t_infomask = 2307, t_hoff = 32 ' ', t_bits = 0x7f0002d7368f "\377\377\003"}
...
(gdb) p *tuple
$10 = {t_choice = {t_heap = {t_xmin = 1, t_xmax = 0, t_field3 = {t_cid = 0, t_xvac = 0}}, t_datum = {datum_len_ = 1,
datum_typmod = 0, datum_typeid = 0}}, t_ctid = {ip_blkid = {bi_hi = 0, bi_lo = 0}, ip_posid = 1}, t_infomask2 = 4,
t_infomask = 2313, t_hoff = 32 ' ', t_bits = 0x7f0002ee632f "\003"}
第七个Tuple
t_infomask = 1282,0x0502,即HEAP_XMAX_COMMITTED | HEAP_XMIN_COMMITTED | HEAP_HASVARWIDTH
(gdb) c
Continuing.
Breakpoint 1, HeapTupleSatisfiesMVCC (htup=0x7ffc56ed8920, snapshot=0x1a062c0, buffer=206) at tqual.c:966
966 HeapTupleHeader tuple = htup->t_data;
(gdb) n
968 Assert(ItemPointerIsValid(&htup->t_self));
(gdb)
969 Assert(htup->t_tableOid != InvalidOid);
(gdb) p *tuple
$11 = {t_choice = {t_heap = {t_xmin = 2360, t_xmax = 2362, t_field3 = {t_cid = 0, t_xvac = 0}}, t_datum = {
datum_len_ = 2360, datum_typmod = 2362, datum_typeid = 0}}, t_ctid = {ip_blkid = {bi_hi = 0, bi_lo = 0},
ip_posid = 1}, t_infomask2 = 8195, t_infomask = 1282, t_hoff = 24 '\030', t_bits = 0x7f0002eba36f ""}
(gdb)
这是被”deleted”的tuple,2362事务已提交,不可见
1086 if (tuple->t_infomask & HEAP_XMAX_IS_MULTI)
(gdb)
1113 if (!(tuple->t_infomask & HEAP_XMAX_COMMITTED))
(gdb)
1141 if (XidInMVCCSnapshot(HeapTupleHeaderGetRawXmax(tuple), snapshot))
(gdb) n
1147 return false;
(gdb)
第八个Tuple
t_infomask = 258,0x0102,即HEAP_XMIN_COMMITTED | HEAP_HASVARWIDTH
(gdb) p *tuple
$12 = {t_choice = {t_heap = {t_xmin = 2361, t_xmax = 2363, t_field3 = {t_cid = 0, t_xvac = 0}}, t_datum = {
datum_len_ = 2361, datum_typmod = 2363, datum_typeid = 0}}, t_ctid = {ip_blkid = {bi_hi = 0, bi_lo = 0},
ip_posid = 3}, t_infomask2 = 16387, t_infomask = 258, t_hoff = 24 '\030', t_bits = 0x7f0002eba347 ""}
(gdb)
这是正在update的tuple,应可见
1080 if (tuple->t_infomask & HEAP_XMAX_INVALID)
(gdb)
1083 if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask))
(gdb)
1086 if (tuple->t_infomask & HEAP_XMAX_IS_MULTI)
(gdb)
1113 if (!(tuple->t_infomask & HEAP_XMAX_COMMITTED))
(gdb)
1115 if (TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetRawXmax(tuple))) --> 非当前事务
(gdb)
1123 if (XidInMVCCSnapshot(HeapTupleHeaderGetRawXmax(tuple), snapshot)) --> 事务快照标志该事务进行中
(gdb)
1124 return true; --> 可见
(gdb)
第九个Tuple
t_infomask = 10242,0x2802,即HEAP_UPDATED | HEAP_XMAX_INVALID | HEAP_HASVARWIDTH
(gdb) p *tuple
$13 = {t_choice = {t_heap = {t_xmin = 2363, t_xmax = 0, t_field3 = {t_cid = 0, t_xvac = 0}}, t_datum = {datum_len_ = 2363,
datum_typmod = 0, datum_typeid = 0}}, t_ctid = {ip_blkid = {bi_hi = 0, bi_lo = 0}, ip_posid = 3},
t_infomask2 = 32771, t_infomask = 10242, t_hoff = 24 '\030', t_bits = 0x7f0002eba31f ""}
(gdb)
这是update操作新生成的tuple,不可见
(gdb) n
971 if (!HeapTupleHeaderXminCommitted(tuple)) --> xmin未提交
(gdb)
973 if (HeapTupleHeaderXminInvalid(tuple))
(gdb)
977 if (tuple->t_infomask & HEAP_MOVED_OFF)
(gdb)
996 else if (tuple->t_infomask & HEAP_MOVED_IN)
(gdb)
1015 else if (TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetRawXmin(tuple))) --> 非当前事务
(gdb)
1057 else if (XidInMVCCSnapshot(HeapTupleHeaderGetRawXmin(tuple), snapshot)) --> xmin事务处于活动中
(gdb)
1058 return false; --> 不可见
(gdb)
查询结果
11:59:38 (xdb@[local]:5432)testdb=# select * from t_mvcc2;
c1 | c2 | c3
----+------+------
2 | C2-2 | C3-2
(1 row)
DONE!
四、参考资料
PG Source Code
免责声明:
① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。
② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341
软考中级精品资料免费领
- 历年真题答案解析
- 备考技巧名师总结
- 高频考点精准押题
- 资料下载
- 历年真题
193.9 KB下载数265
191.63 KB下载数245
143.91 KB下载数1148
183.71 KB下载数642
644.84 KB下载数2756
相关文章
发现更多好内容- Java 中线程间通信的方法有哪些?(java线程间通信的方法是什么)
- Java 多线程批量处理的方法究竟有哪些?(java多线程批量处理的方法是什么)
- Java 中 BigDecimal 的详细介绍与实用使用方法(java中BigDecimal的介绍及使用)
- 如何通过 JavaScript 事件循环来优化代码?(JavaScript 事件循环如何优化代码)
- 如何用 Java 解析 XML 并获取标签属性值?(java怎么解析xml获取标签属性值)
- Java 中实现 MapReduce 的具体方法有哪些?(java实现mapreduce的方法是什么)
- 如何让 Java 的 settimeout 与线程池协同工作?(Java settimeout怎样与线程池配合)
- Java 中对象数组的定义及使用方式有哪些?(Java对象数组定义与用法有哪些)
- Java ClassLoader 的使用方法究竟是什么?(java classloader的使用方法是什么)
- Java 中 Bimap 的适用场景具体有哪些?(Bimap在Java中的适用场景有哪些)