话说 查询“ select cname, comp from test1, test2 where test1.id=test2.id; ” 发送到服务器端, 走查询分支 exec_simple_query ,先调用 start_xact_command 初始化了事务管理相关对象和资源,接着调用 pg_parse_query ,通过 Lex 和 Yacc 对传入 SQL 语句进行词法语法解析,生成解析树。下来调用 GetTransactionSnapshot 方法做内存快照。内存快照和事务隔离级别紧密相关,其中 可重复读和序列化隔离级别每个数据库事务使用一个快照,读未提交和读已提交隔离级别每个语句使用一个快照。序列化隔离级别除快照外还使用 predicate 锁。
1
下面是做内存快照的调用序列图。
Postgres 服务进程简查之开始事务调用序列图
上图红色方框中显示了获取内存快照的方法调用过程,在 GetSnapshotData 方法中根据事务隔离级别等情况获取相应快照,一般 可优化语句(增、删、查、改)都需要快照, 然后注册相应快照。下面是获取快照方法 GetTransactionSnapshot 的流程图。
GetTransactionSnapshot 方法流程图
GetTransactionSnapshot() 方法根据事务隔离级别 分别处理取快照的情况。如果事务隔离级别是 读未提交、读已提交, 调用 GetSnapshotData(&CurrentSnapshotData) 方法获取一个快照赋给 CurrentSnapshot ,返回 CurrentSnapshot 。如果事务隔离级别是 可重复读或序列化 ,且是事务里第一次获取快照,直接返回 CurrentSnapshot ;不是第一次且是 可重复读 事务隔离级别,调用 GetSnapshotData(&CurrentSnapshotData) 方法获取一个快照赋给 CurrentSnapshot ,然后 调用 RegisterSnapshotOnOwner(CurrentSnapshot, TopTransactionResourceOwner) 方法 在资源管理器 TopTransactionResourceOwner 里注册这个快照;不是第一次且是 序列化 事务隔离级别, 调用 RegisterSerializableTransaction(&CurrentSnapshotData) 方法,根据需要调用 GetSnapshotData 方法获取快照并调用 RegisterSnapshotOnOwner 方法在资源属主 TopTransactionResourceOwner 上注册快照,并初始化一个可序列化事务 SERIALIZABLEXACT 结构和哈西表 。最后,不管什么情况都返回当前快照,调用 PushActiveSnapshot 方法,把这个快照推入由 ActiveSnapshotElt 构成的 “活跃快照”栈 。
在 pg 里快照就是数据结构 SnapshotData ,定义如下,表示某个时刻内存里对象的状态,下面是快照 SnapshotData 结构定义:
typedef struct SnapshotData
{
SnapshotSatisfiesFunc satisfies ; /* tuple test function */
/*
* The remaining fields are used only for MVCC snapshots, and are normally
* just zeroes in special snapshots. (But xmin and xmax are used
* specially by HeapTupleSatisfiesDirty.)
*
* An MVCC snapshot can never see the effects of XIDs >= xmax . It can see
* the effects of all older XIDs except those listed in the snapshot.
xmin
* is stored as an optimization to avoid needing to search the XID arrays
* for most tuples.
*/
TransactionId xmin ; /* all XID < xmin are visible to me */
TransactionId xmax ; /* all XID >= xmax are invisible to me */
uint32 xcnt ; /* # of xact ids in xip [] */
TransactionId * xip ; /* array of xact IDs in progress */
/* note: all ids in xip [] satisfy xmin <= xip [i] < xmax */
int32 subxcnt ; /* # of xact ids in subxip [] */
TransactionId * subxip ; /* array of subxact IDs in progress */
bool suboverflowed ; /* has the subxip array overflowed? */
bool takenDuringRecovery ; /* recovery-shaped snapshot? */
/*
* note: all ids in subxip [] are >= xmin , but we don't bother filtering
* out any that are >= xmax
*/
CommandId curcid ; /* in my xact , CID < curcid are visible */
uint32 active_count ; /* refcount on ActiveSnapshot stack */
uint32 regd_count ; /* refcount on RegisteredSnapshotList */
bool copied ; /* false if it's a static snapshot */
} SnapshotData ;
这里面最复杂的是在序列化隔离级别下事务的正常运转,部分体现在 RegisterSerializableTransaction 方法调用了 RegisterSerializableTransactionInt 方法,其流程图在下面
RegisterSerializableTransactionInt 流程图
RegisterSerializableTransactionInt 里,利用 predicate lock 相关的哈西表, PredXactListData 、 PredXactListElementData 等组成的链表结构, RWConflictPoolHeaderData 、 RWConflictData 等组成的链表结构等一起管理 predicate lock 和 serializablexact 结构,(具体个数据结构参见 《 PostgreSQL 启动过程中的那些事七:初始化共享内存和信号九: shmem 中初始化 Predicate 锁 》 ) 利用可序列化快照隔离( Serializable Snapshot Isolation (SSI) ) ,实现了事务的 完全可序列化隔离级别,以支持该事务隔离级别下事务的正常运转。
利用可序列化快照隔离( Serializable Snapshot Isolation (SSI) )的 相关情况 最初是在下面的论文里描述:
Michael J. Cahill , Uwe R 枚 hm , and Alan D. Fekete . 2008.
Serializable isolation for snapshot databases.
In SIGMOD '08: Proceedings of the 2008 ACM SIGMOD
international conference on Management of data,
pages 729-738, New York, NY, USA. ACM.
http://doi.acm.org/10.1145/1376616.1376690
在下面的博士论文里做了进一步阐述。
Michael James Cahill . 2009.
Serializable Isolation for Snapshot Databases.
Sydney Digital Theses.
University of Sydney , School of Information Technologies.
http://hdl.handle.net/2123/5353
就到这儿吧。
------------
转载请注明出处,来自博客:
blog.csdn.net/beiigang
beigang.iteye.com