postgresql源码学习(十七)—— MVCC②-快照与隔离级别简介

67 篇文章 52 订阅
34 篇文章 3 订阅

 一、 快照简介

       快照是记录数据库当前瞬时状态的一个数据结构。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->xmaxSnapshot->xmax=latestCompletedXid+1latestCompletedXid记录的是最大的已提交事务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的快照类型,最重要的一种快照类型。

可见情况:

  •  在创建快照时所有已提交的事务
  • 本事务之前执行的命令 

不可见情况:

  •  在创建快照时尚活跃的事务
  • 在创建快照后启动的事务
  • 当前命令造成的变化(changes made by the current command) 

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章

  • 1
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Hehuyi_In

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值