一、 快照简介
快照是记录数据库当前瞬时状态的一个数据结构。pg的快照主要保存:当前所有活跃事务的最小、最大事务ID、当前活跃事务列表、commandID等。
快照可以分为多种类型,每种快照类型都对应了一种判断元组可见性的方法。
二、 快照结构体
1. 结构体定义
typedef struct SnapshotData *Snapshot;
typedef struct SnapshotData
{
SnapshotType snapshot_type; /* type of snapshot,快照类型 */
/* 用于MVCC控制 */
TransactionId xmin; /* all XID < xmin are visible to me,若事务ID小于xmin,则对当前事务可见 */
TransactionId xmax; /* all XID >= xmax are invisible to me,若事务ID大于xmax,则对当前事务不可见*/
/* 快照生成时对应的活跃事务列表 */
TransactionId *xip;
uint32 xcnt; /* # of xact ids in xip[],xip数组大小 */
/* 活跃的子事务列表 */
TransactionId *subxip;
int32 subxcnt; /* # of xact ids in subxip[],sub xip数组大小*/
bool suboverflowed; /* has the subxip array overflowed? 当子事务过多时,数组有可能overflow */
bool takenDuringRecovery; /* recovery-shaped snapshot? */
bool copied; /* false if it's a static snapshot,如果是静态快照则为false */
CommandId curcid; /* in my xact, CID < curcid are visible */
/*
* An extra return value for HeapTupleSatisfiesDirty, not used in MVCC
* snapshots.,HeapTupleSatisfiesDirty使用的一个变量,在判断可见性的同时,会借助这个变量将元组上的speculativeToken返回给上层
*/
uint32 speculativeToken;
/*
* For SNAPSHOT_NON_VACUUMABLE (and hopefully more in the future) this is
* used to determine whether row could be vacuumed.元组是否可vacuum(用于SNAPSHOT_NON_VACUUMABLE状态)
*/
struct GlobalVisState *vistest;
/*
* Book-keeping information, used by the snapshot manager
*/
uint32 active_count; /* refcount on ActiveSnapshot stack */
uint32 regd_count; /* refcount on RegisteredSnapshots */
pairingheap_node ph_node; /* link in the RegisteredSnapshots heap */
TimestampTz whenTaken; /* timestamp when snapshot was taken,快照生成时间 */
XLogRecPtr lsn; /* position in the WAL stream when taken,快照生成时事务REDO日志的LSN*/
/*
* The transaction completion count at the time GetSnapshotData() built
* this snapshot. Allows to avoid re-computing static snapshots when no
* transactions completed since the last GetSnapshotData().
*/
uint64 snapXactCompletionCount;
} SnapshotData;
#endif /* SNAPSHOT_H */
2. 主要字段含义
快照中最重要的是xmin,xmax,xip(含subxip),它们用来表示当前系统中所有事务所处状态。
testdb=# SELECT txid_current_snapshot();
txid_current_snapshot
-----------------------
100:104:100,102
(1 row)
这3个值就是 xmin:xmax:xip[]数组,注意不要与元组的t_xmin和t_xmax弄混
- Snapshot->xmin:当前所有活跃事务中最小的事务txid,所有比它更早的事务(xid<xmin),要么已提交要么已回滚,因此均可见。
- Snapshot->xmax:Snapshot->xmax=latestCompletedXid+1,latestCompletedXid记录的是最大的已提交事务id,因此Snapshot->xmax就比所有已提交事务的事务id都要大。如果某个事务xid>= Snapshot->xmax,说明在快照创建时这个事务还是活跃事务(或尚未启动)因此其结果对当前事务不可见。
- xip[]数组:快照创建时,所有活跃事务的事务id列表,该列表仅包含[xmin,xmax)范围内的活跃事务xid。
来看一个小例子
100:100: 含义如下
- xmin=100,所以txid<100的事务均不活跃
- xmax=100,所以txid>=100的事务均活跃或未启动
- xip_list为空,表示[xmin,xmax)范围内无活跃事务
100:104:100,102含义如下
- xmin=100,所以txid<100的事务均不活跃
- xmax=104,所以txid>=104的事务均活跃或未启动
- xip_list为100,102,表示[xmin,xmax)范围内100,102为活跃事务
3. 快照与隔离级别
pg会根据不同隔离级别设置,获取不同时刻的快照:
- 已提交读:在该事务的每条SQL执行之前都会重新获取一次快照
- 可重复读和可串行化:该事务只在第一条SQL执行之前获取一次快照
三、 快照类型
前面提到过,快照可以分为多种类型,每种快照类型都对应了一种判断元组可见性的方法,快照类型结构体在SnapshotType中定义。
typedef enum SnapshotType
{
SNAPSHOT_MVCC = 0,
SNAPSHOT_SELF,
SNAPSHOT_ANY,
SNAPSHOT_TOAST,
SNAPSHOT_DIRTY,
SNAPSHOT_HISTORIC_MVCC,
SNAPSHOT_NON_VACUUMABLE
} SnapshotType;
各类型快照可见性判断条件:
快照类型 | 说明 |
SNAPSHOT_MVCC | 用于实现MVCC的快照类型,最重要的一种快照类型。 可见情况:
不可见情况:
|
SNAPSHOT_SELF | 它的可见性与快照创建的时间关系不大,主要取决于元组上记录的事务版本信息。 可见情况:
不可见情况:
|
SNAPSHOT_ANY | 扫描到的元组均可见,与元组的版本及快照信息均无关 |
SNAPSHOT_TOAST | 用于toast元组的检查,toast元组的可见性依赖主表中元组的可见性,因此可以简化判断逻辑 |
SNAPSHOT_DIRTY | 可见情况:
跟SNAPSHOT_SELF的区别在于它还可见活跃事务(这也是它叫脏快照的原因,相当于脏读) |
SNAPSHOT_HISTORIC_MVCC | 用于逻辑复制中逻辑解码的可见性判断 |
SNAPSHOT_NON_VACUUMABLE | 判断元组是否对一些事务可见,例如死元组(VACUUMABLE状态)则不可见 |
三、 隔离级别查看与设置
查看数据库事务隔离级别
SELECT name, setting FROM pg_settings WHERE name ='default_transaction_isolation';
-- 或
SELECT current_setting('default_transaction_isolation');
设置全局事务隔离级别
法一:
修改 postgresql.conf文件 中的 default_transaction_isolation
法二:
alter system set default_transaction_isolation to 'REPEATABLE READ';
查看当前会话事务隔离级别
show transaction_isolation
或
SELECT current_setting('transaction_isolation');
设置当前会话事务隔离级别
SET SESSION CHARACTERISTICS AS TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;
设置当前事务的事务隔离级别
START TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;
或
BEGIN ISOLATION LEVEL READ UNCOMMITTED READ WRITE;
参考
《postgresql实战》
The Internals of PostgreSQL : Chapter 5 Concurrency Control
《PostgreSQL技术内幕:事务处理深度探索》第3章