PostgreSQL–可重复读隔离级别的实现

PostgreSQL–可重复读隔离级别的实现

2017-04-23 20:03:07 | 分类: 数据库 | 标签: | 举报 | 字号 订阅




用微信 “扫一扫”

将文章分享到朋友圈。

用易信 “扫一扫”

将文章分享到朋友圈。

下载LOFTER
我的照片书 |

可重复读的实现

在可重复读隔离级别下, PostgreSQL 获取快照,然后在事务内部一直使用同一个快照,这样所有的读操作都使用同一个快照,避免了这个事务中间某个时间点上,其他事务提交的数据对本事务造成影响。如下代码分析,重点在于 FirstSnapshotSet FirstXactSnapshot 的获取以及使用上。

Snapshot

GetTransactionSnapshot (void) // 根据隔离级别获取事务快照

{…

if ( HistoricSnapshotActive ()) // 有历史快照 [1] ,则使用历史上定制的快照,因为逻辑复制技术的主备机制不支持序列化隔离级别 [2]

{

Assert(!FirstSnapshotSet);

return HistoricSnapshot;

}

if (! FirstSnapshotSet )// 是否使用同一个快照?如果有 FirstXactSnapshot ,则使用同一个不再创建,以保证整个事务块内看到的都是一致的数据

{…

// 注意下面对于不同隔离级别的快照的获取,都将调用 GetSnapshotData() 获取快照,只是不同隔离级别需要做不同的检查,或者

if ( IsolationUsesXactSnapshot ()) // 隔离级别需要大于等于可重复读“ REPEATABLE READ ”,即至少是可重复读或序列化

{

/* First, create the snapshot in CurrentSnapshotData */

if ( IsolationIsSerializable ()) // 隔离级别是序列化

CurrentSnapshot = GetSerializableTransactionSnapshot [3] (&CurrentSnapshotData); // 间接调用 GetSnapshotData() 获取快照

else

CurrentSnapshot = GetSnapshotData (&CurrentSnapshotData); // 隔离级别是可重复读。直接调用 GetSnapshotData() 获取快照

/* Make a saved copy */

CurrentSnapshot = CopySnapshot (CurrentSnapshot);

FirstXactSnapshot = CurrentSnapshot;

}

else

CurrentSnapshot = GetSnapshotData (&CurrentSnapshotData); // 隔离级别是读已提交“ READ COMMITTED

/* Don’t allow catalog snapshot to be older than xact snapshot. */

CatalogSnapshotStale = true;

FirstSnapshotSet = true; // 完成一个事务内第一次获取快照的任务

return CurrentSnapshot;

}

if ( IsolationUsesXactSnapshot ()) // 第一次之后再次获取快照的可重复读和序列化隔离级别,则不用再生成新的快照,使用老的快照即可。此处是区别于读已提交隔离级别的重要点之一,在一个事务块内的多个 SQL 语句是否使用“同一个”快照( FirstXactSnapshot ),是可重复读(包括序列化)和读已提交之间的最大差别

return CurrentSnapshot ;

/* Don’t allow catalog snapshot to be older than xact snapshot. */

CatalogSnapshotStale = true;

CurrentSnapshot = GetSnapshotData(&CurrentSnapshotData);// 第一次之后再次获取快照的读已提交隔离级别,则生成新的快照。每次都生成新快照

return CurrentSnapshot;

}

在上级函数调用 exec_simple_query() ,即同一个事务内部多次执行查询的时候, snapshot 给定的参数值为 InvalidSnapshot ,所以通过调用 GetTransactionSnapshot () GetActiveSnapshot () 获取的都是当前同一个快照。

void // exec_simple_query ()调用本函数时,传递参数“ PortalStart(portal, NULL, 0, InvalidSnapshot); ”,

PortalStart (Portal portal, ParamListInfo params, int eflags, Snapshot snapshot ) // 即“ snapshot == InvalidSnapshot

{…

switch (portal->strategy)

{

case PORTAL_ONE_SELECT:

/* Must set snapshot before starting executor. */

if (snapshot) // snapshot 给定的参数值为 InvalidSnapshot 时,不走此分之

PushActiveSnapshot(snapshot);

else // 可重复读级别, “ GetTransactionSnapshot () ”则一直返回的是“ FirstXactSnapshot ”,所以快照使用的是同一个

PushActiveSnapshot( GetTransactionSnapshot ());

/* Create QueryDesc in portal’s context; for the moment, set the destination to DestNone. */

queryDesc = CreateQueryDesc((PlannedStmt *) linitial(portal->stmts), portal->sourceText,

GetActiveSnapshot (),

InvalidSnapshot, None_Receiver, params, 0);

}

}

[1] 历史快照,用于复制技术中解析 WAL 日志中与事务相关部分的内容。

[2] 技术上看,主备机制做读写分离时,会存在跨节点的读书据异常现象。在 PostgreSQL MVCC 为主要的并发控制技术这种机制下,主备之间主写备读的使用方式同样会出现写偏序异常。解决的办法是在备机的读操作的事务信息需要告知主机,由主机统一协调(协调的方式类似 SSI 技术),这样才能保持真正的数据一致性。这是未来 PostgreSQL 需要弥补的一点。

[3] 参考 8.7.2 节对序列化快照函数的分析,对比直接调用 GetSnapshotData() 实现可重复读隔离级别的差异,以此领悟 SSI 技术的本质。

评论这张

阅读( 6 ) | 评论( 0 )




用微信 “扫一扫”

将文章分享到朋友圈。

用易信 “扫一扫”

将文章分享到朋友圈。

喜欢 推荐 转载

历史上的今天

在LOFTER的更多文章

评论

稿源:那海蓝蓝的博客 (源链) | 关于 | 阅读提示

本站遵循[CC BY-NC-SA 4.0]。如您有版权、意见投诉等问题,请通过eMail联系我们处理。
酷辣虫 » 后端存储 » PostgreSQL–可重复读隔离级别的实现

喜欢 (0)or分享给?

专业 x 专注 x 聚合 x 分享 CC BY-NC-SA 4.0

使用声明 | 英豪名录