Dans le cas le plus simple, lorsque nous insérons une nouvelle ligne dans une table (et que la transaction est validée), elle sera visible pour toutes les transactions suivantes. Voir xmax
être 0 dans cet exemple:
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
Lorsque nous le mettons à jour (car l'indicateur a été défini FALSE
par accident), il change un peu:
UPDATE vis SET is_active = TRUE;
SELECT ctid, xmin, xmax, * FROM vis;
ctid │ xmin │ xmax │ id │ is_active
──────┼──────┼──────┼────┼───────────
(0,2) │ 2700 │ 0 │ 1 │ t
Selon le modèle MVCC utilisé par PostgreSQL, une nouvelle ligne physique a été écrite et l'ancienne invalidée (cela peut être vu sur ctid
). Le nouveau est toujours visible pour toutes les transactions ultérieures.
Maintenant, il se passe quelque chose d'intéressant lorsque nous annulons 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
La version en ligne reste la même, mais xmax
est maintenant définie sur quelque chose. Malgré cela, les transactions suivantes peuvent voir cette ligne (sinon inchangée).
Après avoir lu un peu à ce sujet, vous pouvez comprendre quelques éléments sur la visibilité des lignes. Il y a la carte de visibilité , mais elle indique seulement si une page entière est visible - elle ne fonctionne certainement pas au niveau de la ligne (tuple). Ensuite, il y a le journal de validation (aka clog
) - mais comment Postgres détermine-t-il s'il doit le visiter?
J'ai décidé de jeter un coup d'œil aux bits du masque d'information pour comprendre comment la visibilité fonctionne réellement. Pour les voir, le plus simple est d'utiliser l' extension pageinspect . Afin de savoir quels bits sont définis, j'ai créé une table pour les stocker:
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));
Ensuite, j'ai vérifié ce qu'il y avait dans ma vis
table - notez que cela pageinspect
montre le contenu physique du tas, donc non seulement les lignes visibles sont retournées:
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
Ce que je comprends de ce qui précède, c'est que la première version a pris vie avec la transaction 2699, puis a été remplacée avec succès par la nouvelle version à 2700.
Ensuite, la suivante, qui était en vie depuis 2700, a eu une tentative d' UPDATE
annulation de 2702, vue de HEAP_XMAX_INVALID
.
Le dernier n'est jamais vraiment né, comme le montre HEAP_XMIN_INVALID
.
Donc, à en juger par ce qui précède, le premier et le dernier cas sont évidents - ils ne sont plus visibles pour la transaction 2703 ou supérieure.
Le second doit être recherché quelque part - je suppose que c'est le journal de validation, aka clog
.
Pour compliquer davantage les problèmes, un UPDATE
résultat ultérieur dans les éléments suivants:
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
Ici, je vois déjà deux candidats qui pourraient être visibles. Alors, enfin, voici mes questions:
- Est-ce que je suppose que
clog
c'est le lieu à considérer pour déterminer la visibilité dans ces cas? - Quels drapeaux (ou combinaison de drapeaux) indiquent au système de visiter le
clog
? - Existe-t-il un moyen d'examiner ce qu'il y a à l'intérieur
clog
? Il y a des mentions sur laclog
corruption dans les versions antérieures de Postgres et un indice que l'on peut créer un faux fichier manuellement. Cette information nous aiderait beaucoup.