PostgreSQL MVCC可见性判断规则

PostgreSQL 扫描 tuple 元组时通过一定的规则来判断该元组是否可见,满足可见性规则的则输出给用户,不满足可见性规则的则跳过。内核在实现可见性判断规则上根据快照的类型提供了一组可见性判断函数,这些函数有相同的参数类型,传入 tuple 元组、Snapshot 快照和元组所在的 buffer,即可根据相应的可见性规则给出该元组的可见性结果。

可见性判断函数:

  • HeapTupleSatisfiesMVCC,对应快照类型 SNAPSHOT_MVCC
  • HeapTupleSatisfiesSelf,对应快照类型 SNAPSHOT_SELF
  • HeapTupleSatisfiesAny,对应快照类型 SNAPSHOT_ANY
  • HeapTupleSatisfiesToast,对应快照类型 SNAPSHOT_TOAST
  • HeapTupleSatisfiesDirty,对应快照类型 SNAPSHOT_DIRTY
  • HeapTupleSatisfiesHistoricMVCC,对应快照类型 SNAPSHOT_HISTORIC_MVCC
  • HeapTupleSatisfiesNonVacuumable,对应快照类型 SNAPSHOT_NON_VACUUMABLE

上述可见性判断函数中使用最广泛的是 HeapTupleSatisfiesMVCC,在介绍该函数的可见性判断规则之前,先了解一下元组中的几个重要的信息,如下:

  • xmin,表示 insert 元组的事务号
  • xmmx,表示 update 或者 delete 元组的事务号
  • cmin,表示 insert 元组的 SQL 在事务中的序号
  • cmax,表示 update 或者 delete 元组的 SQL 在事务中的序号
  • t_infomask,元组的标记位,包含各种标记信息

MVCC 可见性判断主要就是将元组的 xmin、xmax、cmin、cmax 以及 t_infomask 信息结合快照 snapshot 以及 CLOG 中的信息,根据可见性判断规则,决定一个元组是否可见。

可见性判断主要分为两个部分:

  1. 对元组的 xmin 进行可见性判断,即 insert 该元组的事务是否已提交,是否可见。
  2. 对元组的 xmax 进行可见性判断,即该元组是否已被 update 或者 delete。

下面是元组可见性判断的详细规则,为了简化逻辑,去除了一些兼容相关的干扰以及很少走到的一些代码路径。

## 以下是关于元组 xmin 的判断规则

1. 如果元组 xmin 未提交,判断条件为:!(t_infomask & HEAP_XMIN_COMMITTED)
    1.1 如果元组的 xmin 无效,即 HeapTupleHeaderXminInvalid(tuple),返回不可见
    1.2 如果元组的 xmin 在当前事务(包含子事务)中
        1.2.1 如果元组的 cmin 大于等于 快照的 commandId,返回不可见
        1.2.2 如果元组的 t_infomask 包含 HEAP_XMAX_INVALID 标记,即 xmax 无效,返回可见
        1.2.3 如果元组的 t_infomask 仅包含 HEAP_XMAX_EXCL_LOCK,返回可见
        1.2.4 如果元组的 xmax 不在当前事务中,设置标记 HEAP_XMAX_INVALID,返回可见
        1.2.5 如果元组的 cmax 大于等于快照的 commandId,返回可见,否则返回不可见
    1.3 如果元组的 xmin 在快照的活跃事务列表中,返回不可见
    1.4 如果元组的 xmin 在 clog 中已提交,设置标记 HEAP_XMIN_COMMITTED
    1.5 设置标记 HEAP_XMIN_INVALID,返回不可见
2. 如果元组的 xmin 已提交
    2.1 如果元组的 xmin 未冻结并且 xmin 在快照活跃事务列表中,返回不可见

## 到此说明元组的 xmin 已提交,即 insert 已提交,是否可见还得看 xmax,下面是关于 xmax 的判断规则

3. t_infomask 标记包含 HEAP_XMAX_INVALID,返回可见
4. t_infomask 标记满足 HEAP_XMAX_IS_LOCKED_ONLY,返回可见
5. 如果 t_infomask 不包含 HEAP_XMAX_COMMITTED
    5.1 如果 xmax 在当前事务中
        5.1.1 cmax 大于等于快照的cid,返回可见
        5.1.2 cmax 小于快照的 cid,返回不可见
    5.2 如果 xmax 在快照的活跃事务列表中,返回可见
    5.3 如果 xmax 在 clog 中没有提交,设置标记 HEAP_XMAX_INVALID,返回可见
    5.4 设置标记 HEAP_XMAX_COMMITTED
6. t_infomask 包含 HEAP_XMAX_COMMITTED
    6.1 如果 xmax 在快照的活跃事务列表中,返回可见

## 除了以上描述的情况外,其他的都返回不可见

7. 其他都是返回不可见

以上分析可以看出 MVCC 可见性判断的规则还是比较复杂的,考虑了很多场景,规则之间还有先后顺序依赖。总结可见性规则影响因素如下:

  • xmin 是否已提交,是否有效
  • xmin 是否在当前会话的事务中
  • xmin 是否在快照活跃事务列表中
  • xmin 在 clog 中的状态
  • cmin 与 快照 cid 的大小比较
  • xmax 是否已提交,是否有效
  • xmax 是否在当前会话的事务中
  • xmax 是否在快照活跃事务列表中
  • xmax 在 clog 中的状态
  • cmax 与快照 cid 的大小比较

关于可见性分析,有两点需要注意:

  1. 即使是 select 查询,也可能会写 wal 日志,比如设置 HEAP_XMIN_COMMITTED 这个操作就会涉及写 wal 日志。
  2. 此外即使 insert 事务已提交,已经写入 CLOG,但是该事务的事务号还没有从活跃事务列表中删除,那么元组仍然是不可见的。

注: 本文涉及的源码版本为 PostgreSQL 13.3

文章评论

0条评论