支持SSI技术的可序列化的事务的表示
在事务的隔离级别为SERIALIZABLE时,PostgreSQL为支持SSI技术,使用“SERIALIZABLEXACT”表示可序列化的事务,并标识事务与读写冲突之间的关系。
typedef struct SERIALIZABLEXACT // SERIALIZABLEXACT是一个可序列化的事务
{
VirtualTransactionId vxid; /* The executing process always has one of these. */ //虚拟的事务ID
/*
* We use two numbers to track the order that transactions commit. Before
* commit, a transaction is marked as prepared, and prepareSeqNo is set.
* Shortly after commit, it's marked as committed, and commitSeqNo is set.
* This doesn't give a strict commit order, but these two values together
* are good enough for us, as we can always err on the safe side and
* assume that there's a conflict, if we can't be sure of the exact
* ordering of two commits.
*
* Note that a transaction is marked as prepared for a short period during
* commit processing, even if two-phase commit is not used. But with
* two-phase commit, a transaction can stay in prepared state for some
* time.
*/
//以下两个变量,用以表明事务的提交顺序
SerCommitSeqNo prepareSeqNo; //被PreCommit_CheckForSerializationFailure()函数赋值
SerCommitSeqNo commitSeqNo; //被ReleasePredicateLocks()函数赋值
/* these values are not both interesting at the same time */
union
{
SerCommitSeqNo earliestOutConflictCommit; /* when committed with conflict out */
SerCommitSeqNo lastCommitBeforeSnapshot; /* when not committed or no conflict out */
}SeqNo;
// outConflicts和inConflicts用以表示事务间的相互影响, inConflicts是本事务影响了其他事务, outConflicts是其他事务影响了本事务
SHM_QUEUE outConflicts; /* 数据不能被读的“写事务的列表”,即想要读的数据因被别的事务写过则本事务不能读取 */
SHM_QUEUE inConflicts; /* 不能看到本事务写的数据的“读事务的列表”,即本事务写过的数据不能被其他事务看到 */
SHM_QUEUE predicateLocks; //事务上的谓词锁列表
SHM_QUEUE finishedLink; /* list link in FinishedSerializableTransactions */ //“FinishedSerializableTransactions”是session中全局共享的事务列表,表示已经完成的可序列化的事务,即已经提交的事务。特别注意,已经完成的事务,依旧可能会和正在执行的事务构成读写冲突(这是快照隔离技术的缺陷,因此才诞生了可序列化的快照隔离、即SSI技术)
/*
* for r/o transactions: list of concurrent r/w transactions that we could
* potentially have conflicts with, and vice versa for r/w transactions
*/
SHM_QUEUE possibleUnsafeConflicts; //对于只读事务而言,存在可能的不安全的冲突(读写事务带来的不安全的冲突)
TransactionId topXid; /* top level xid for the transaction, if one exists; else invalid */
TransactionId finishedBefore; //本事务之前已经完成的事务ID
TransactionId xmin; /* the transaction's snapshot xmin */
uint32 flags; /* OR'd combination of values defined below */
int pid; //进程的ID
} SERIALIZABLEXACT;
读写冲突
PostgreSQL使用“RWConflictData”表示事务之间的读写冲突关系,这样的关系是两个事务之间的一条“边”,如果有多个事务,则事务之间的读写冲突就有出边和入边,所以表示出边关系的outLink和sxactOut都是指向另外一个事务的,表示入边关系的inLink和sxactIn都是其他事务指向本事务的一个读写冲突。
一个读写冲突,是指t1事务要读t2事务写的数据,这样导致t1的outLink指向t2,t2的inLink指向t1。如果写操作先发生(t2事务发生在前,通过MVCC机制,发生读同一个数据的事务t2的读操作能够被检测出来,即不允许发生 ?? 试验)
typedef struct RWConflictData //事务之间,表示:“读写”冲突。是两个事务之间因“读写冲突”建立起的联系
{
SHM_QUEUE outLink; //自一个“sxact”发出的冲突链接
SHM_QUEUE inLink; //指向一个“sxact”的冲突链接
SERIALIZABLEXACT *sxactOut; // SERIALIZABLEXACT是一个可序列化的事务,sxactOut表示本事务引发的一个冲突,指向了其他事务
SERIALIZABLEXACT *sxactIn; // SERIALIZABLEXACT是一个可序列化的事务,sxactIn表示指向本事务的冲突
}RWConflictData;
在PostgreSQL中,所有的读写冲突最后汇聚在“RWConflictPool”读写冲突池中。
typedef struct RWConflictData *RWConflict;
typedef struct RWConflictPoolHeaderData
{
SHM_QUEUE availableList;
RWConflict element;
}RWConflictPoolHeaderData; -----------------------
↓
typedef struct RWConflictPoolHeaderData *RWConflictPoolHeader;
static RWConflictPoolHeader RWConflictPool; //读写冲突池,一个session中全局共享
而读写冲突池的操作关系如图X-XX所示,从图中可以看出:
q 获取逻辑元组的时候(如调用heapgettup()函数),通过CheckForSerializableConflictOut()函数来检测和设置读写冲突
q 在隔离级别为序列化、通过GetTransactionSnapshot()函数获取事务的快照的时候,调用SetPossibleUnsafeConflict()函数设置事务间读写冲突的关系