dez*_*zso 10 postgresql transaction-log mvcc
在最简单的情况下,当我们向表中插入新行(并且事务提交)时,它将对所有后续事务可见。请参阅xmax
此示例中的 0:
CREATE TABLE vis (
id serial,
is_active boolean
);
INSERT INTO vis (is_active) VALUES (FALSE);
SELECT ctid, xmin, xmax, * FROM vis;
ctid ?xmin ? xmax ? id ? is_active
?????????????????????????????????????
(0,1) ?2699 ? 0 ? 1 ? f
Run Code Online (Sandbox Code Playgroud)
当我们更新它时(因为该标志是FALSE
偶然设置的),它会发生一些变化:
UPDATE vis SET is_active = TRUE;
SELECT ctid, xmin, xmax, * FROM vis;
ctid ? xmin ? xmax ? id ? is_active
?????????????????????????????????????
(0,2) ? 2700 ? 0 ? 1 ? t
Run Code Online (Sandbox Code Playgroud)
根据PostgreSQL 使用的MVCC模型,一个新的物理行被写入,旧的无效(这可以从 中看出ctid
)。新的仍然对所有后续事务可见。
现在,当我们回滚时发生了一件有趣的事情UPDATE
:
BEGIN;
UPDATE vis SET is_active = TRUE;
ROLLBACK;
SELECT ctid, xmin, xmax, * FROM vis;
ctid ? xmin ? xmax ? id ? is_active
??????????????????????????????????????
(0,2) ? 2700 ? 2702 ? 1 ? t
Run Code Online (Sandbox Code Playgroud)
行版本保持不变,但现在xmax
设置为某些内容。尽管如此,后续事务仍可以看到此行(否则未更改)。
在阅读了一些关于此的内容后,您可能会了解有关行可见性的一些信息。有可见性映射,但它只告诉整个页面是否可见 - 它绝对不适用于行(元组)级别。然后是提交日志(又名clog
) - 但是 Postgres 如何确定它是否必须访问它?
我决定查看信息掩码位,以了解可见性实际是如何工作的。要查看它们,最简单的方法是使用pageinspect 扩展。为了找出设置了哪些位,我创建了一个表来存储它们:
CREATE TABLE infomask (
i_flag text,
i_bits bit(16)
);
INSERT INTO infomask
VALUES
('HEAP_HASNULL', x'0001'::bit(16)),
('HEAP_HASVARWIDTH', x'0002'::bit(16)),
('HEAP_HASEXTERNAL', x'0004'::bit(16)),
('HEAP_HASOID', x'0008'::bit(16)),
('HEAP_XMAX_KEYSHR_LOCK', x'0010'::bit(16)),
('HEAP_COMBOCID', x'0020'::bit(16)),
('HEAP_XMAX_EXCL_LOCK', x'0040'::bit(16)),
('HEAP_XMAX_LOCK_ONLY', x'0080'::bit(16)),
('HEAP_XMIN_COMMITTED', x'0100'::bit(16)),
('HEAP_XMIN_INVALID', x'0200'::bit(16)),
('HEAP_XMAX_COMMITTED', x'0400'::bit(16)),
('HEAP_XMAX_INVALID', x'0800'::bit(16)),
('HEAP_XMAX_IS_MULTI', x'1000'::bit(16)),
('HEAP_UPDATED', x'2000'::bit(16)),
('HEAP_MOVED_OFF', x'4000'::bit(16)),
('HEAP_MOVED_IN', x'8000'::bit(16)),
('HEAP_XACT_MASK', x'FFF0'::bit(16));
Run Code Online (Sandbox Code Playgroud)
然后检查我的vis
表pageinspect
中的内容- 请注意,它显示了堆的物理内容,因此不仅返回了可见行:
SELECT t_xmin, t_xmax, string_agg(i_flag, ', ') FILTER (WHERE (t_infomask::bit(16) & i_bits)::integer::boolean)
FROM heap_page_items(get_raw_page('vis', 0)),
infomask
GROUP BY t_xmin, t_xmax;
t_xmin ? t_xmax ? string_agg
????????????????????????????????????????????????????????????????????????
2699 ? 2700 ? HEAP_XMIN_COMMITTED, HEAP_XMAX_COMMITTED
2700 ? 2702 ? HEAP_XMIN_COMMITTED, HEAP_XMAX_INVALID, HEAP_UPDATED
2702 ? 0 ? HEAP_XMIN_INVALID, HEAP_XMAX_INVALID, HEAP_UPDATED
Run Code Online (Sandbox Code Playgroud)
我从上面的理解是,第一个版本在事务 2699 时生效,然后在 2700 成功替换为新版本。
然后从 2700 开始存活的下一个版本UPDATE
在 2702有回滚尝试,从HEAP_XMAX_INVALID
.
最后一个从未真正出生,如 所示HEAP_XMIN_INVALID
。
因此,从上面的猜测,第一种和最后一种情况是显而易见的 - 它们对事务 2703 或更高版本不再可见。
第二个必须在某处查找 - 我想它是提交日志,又名clog
.
为了使问题进一步复杂化,随后的UPDATE
结果如下:
t_xmin ? t_xmax ? string_agg
??????????????????????????????????????????????????????????????????????
2699 ? 2700 ? HEAP_XMIN_COMMITTED, HEAP_XMAX_COMMITTED
2702 ? 0 ? HEAP_XMIN_INVALID, HEAP_XMAX_INVALID, HEAP_UPDATED
2703 ? 0 ? HEAP_XMAX_INVALID, HEAP_UPDATED
2700 ? 2703 ? HEAP_XMIN_COMMITTED, HEAP_UPDATED
Run Code Online (Sandbox Code Playgroud)
在这里,我已经看到两个可能可见的候选人。所以,最后,这是我的问题:
clog
是在这些情况下确定可见性的地方吗?clog
?clog
?clog
在早期版本的 Postgres中提到了损坏,并暗示可以手动构建假文件。这条信息将对此有很大帮助。因此,从上面的猜测,第一种和最后一种情况是显而易见的 - 它们对事务 2703 或更高版本不再可见。第二个必须在某处查找 - 我想它是提交日志,又名堵塞。
第二个有HEAP_XMAX_INVALID
。这意味着它不必咨询堵塞物,因为有人已经这样做了,看到它xmax
被中止,并设置一个“提示位”,以便未来的进程不需要再次访问该行的堵塞物。
哪些标志(或标志的组合)告诉系统访问堵塞?
如果没有heap_xmin_committed
or heap_xmin_invalid
,那么您必须访问 clog 以查看 xmin 的配置。如果事务仍在进行中,则该行对您不可见,并且您不能设置任何标志。如果事务提交或回滚,则相应地设置heap_xmin_committed
或heap_xmin_invalid
(如果这样做方便的话——这不是强制性的),这样以后的人就不需要查找它了。
如果xmin
有效且已提交,如果xmax
不为零,并且没有heap_max_committed
or heap_max_invalid
,那么您必须访问阻塞以查看该事务的处置情况。
有没有办法检查木屐里面的东西?在早期版本的 Postgres 中提到了堵塞损坏,并暗示可以手动构建假文件。这条信息将对此有很大帮助。
我不知道这样做的用户友好的方式。您可以使用“od”以合适的方式转储堵塞文件以检查它们,并使用中定义的宏找出检查的位置src/backend/access/transam/clog.c
我很惊讶 PGXN 上没有可以为您工作的扩展,但我找不到。但我认为它不会那么有用,因为您确实需要能够在服务器未运行时执行此操作。
看一下HeapTupleSatisfiesMVCC()实现:实际clog
检查发生在TransactionIdDidCommit()中,但仅当无法从信息掩码位推断事务状态时才会调用它(HeapTupleHeaderXminCommited() 宏 和朋友)。
我追踪了对pg_clog
函数TransactionDidCommit()
和的访问TransactionDidAbort()
,然后我查找了这些函数的调用位置,代码中与您的问题相关的唯一位置似乎是在 中HeapTupleSatisfiesMVCC()
。从这个函数的代码中,您可以看到,只有当元组没有设置相关的 infomask 位时,才会发生实际的阻塞查找:代码首先使用HeapTupleHeaderXminCommitted()
et al 检查这些位。仅当未设置位时才会发生阻塞查找。