Wenn wir im einfachsten Fall eine neue Zeile in eine Tabelle einfügen (und die Transaktion festschreibt), ist sie für alle nachfolgenden Transaktionen sichtbar. Siehe xmax
0 in diesem Beispiel sein:
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
Wenn wir es aktualisieren (weil das Flag versehentlich gesetzt FALSE
wurde), ändert es sich ein wenig:
UPDATE vis SET is_active = TRUE;
SELECT ctid, xmin, xmax, * FROM vis;
ctid │ xmin │ xmax │ id │ is_active
──────┼──────┼──────┼────┼───────────
(0,2) │ 2700 │ 0 │ 1 │ t
Gemäß dem von PostgreSQL verwendeten MVCC- Modell wurde eine neue physische Zeile geschrieben und die alte ungültig gemacht (dies ist aus dem ersichtlich ctid
). Die neue ist weiterhin für alle nachfolgenden Transaktionen sichtbar.
Jetzt passiert etwas Interessantes, wenn wir Folgendes zurücksetzen 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
Die Zeilenversion bleibt gleich, ist aber jetzt xmax
auf etwas eingestellt. Trotzdem können nachfolgende Transaktionen diese (ansonsten unveränderte) Zeile sehen.
Nachdem Sie ein wenig darüber gelesen haben, können Sie einige Dinge über die Zeilensichtbarkeit herausfinden. Es gibt die Sichtbarkeitskarte , die jedoch nur angibt, ob eine ganze Seite sichtbar ist - sie funktioniert definitiv nicht auf Zeilenebene (Tupel). Dann gibt es das Commit-Protokoll (auch bekannt als clog
) - aber wie findet Postgres heraus, ob es es besuchen muss?
Ich beschloss, mir die Infomask-Bits anzusehen, um herauszufinden, wie die Sichtbarkeit tatsächlich funktioniert. Um sie zu sehen, ist es am einfachsten, die pageinspect-Erweiterung zu verwenden . Um herauszufinden, welche Bits gesetzt sind, habe ich eine Tabelle zum Speichern erstellt:
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));
Überprüfen Sie dann, was sich in meiner vis
Tabelle befindet. Beachten Sie, dass pageinspect
der physische Inhalt des Heaps angezeigt wird, sodass nicht nur die sichtbaren Zeilen zurückgegeben werden:
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
Was ich aus dem Obigen verstehe, ist, dass die erste Version mit der Transaktion 2699 zum Leben erweckt und dann bei
2700 erfolgreich durch die neue Version ersetzt wurde. Dann hatte die nächste, die seit 2700 am Leben war, einen Rollback-Versuch von UPDATE
2702, gesehen von HEAP_XMAX_INVALID
.
Der letzte wurde nie wirklich geboren, wie gezeigt HEAP_XMIN_INVALID
.
Aus den obigen Ausführungen geht hervor, dass der erste und der letzte Fall offensichtlich sind - sie sind für die Transaktion 2703 oder höher nicht mehr sichtbar.
Der zweite muss irgendwo nachgeschlagen werden - ich nehme an, es ist das Commit-Protokoll, auch bekannt als clog
.
Um die Probleme weiter zu verkomplizieren, führt eine nachfolgende UPDATE
zu folgenden Ergebnissen:
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
Hier sehe ich bereits zwei Kandidaten, die sichtbar sein könnten. Also, endlich, hier sind meine Fragen:
- Ist meine Annahme, dass dies der
clog
richtige Ort ist, um die Sichtbarkeit in diesen Fällen zu bestimmen? - Welche Flags (oder Kombinationen von Flags) weisen das System an, das zu besuchen
clog
? - Gibt es eine Möglichkeit zu untersuchen, was sich in der
clog
? Es gibt Erwähnungen überclog
Korruption in früheren Versionen von Postgres und einen Hinweis, dass man eine gefälschte Datei manuell erstellen kann. Diese Information würde viel dabei helfen.