在最简单的情况下,当我们在表中插入新行(并且提交事务)时,所有后续事务都将看到该行。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
当我们更新它时(因为该标志被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
根据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
行版本保持不变,但现在xmax
设置为某些版本。尽管如此,后续事务仍可以看到该行(否则保持不变)。
阅读一些有关此内容的知识后,您可能会发现有关行可见性的一些信息。有可见性图,但是它只告诉整个页面是否可见-它绝对不能在行(元组)级别上工作。然后是提交日志(aka clog
)-但是Postgres如何确定是否必须访问它?
我决定看一下infomask位,以了解可见性实际上是如何工作的。要查看它们,最简单的方法是使用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));
然后检查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
从上面的内容中我了解到,第一个版本以交易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
在这里,我已经看到了两个可见的候选人。所以,最后,这是我的问题:
- 我是否假设
clog
在这种情况下可以确定可视性? - 哪些标志(或标志组合)告诉系统访问
clog
? - 有没有办法检查里面有
clog
什么?clog
在早期版本的Postgres 中提到了损坏问题,并暗示可以手动构建假文件。这条信息将对其有很大帮助。