Wie genau wird die Zeilensichtbarkeit bestimmt?

10

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 xmax0 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 FALSEwurde), ä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 xmaxauf 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 visTabelle befindet. Beachten Sie, dass pageinspectder 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 UPDATE2702, 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 UPDATEzu 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 clogrichtige 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 über clogKorruption 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.
dezso
quelle

Antworten:

6

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.

Der 2. hat HEAP_XMAX_INVALID. Das bedeutet, dass der Clog nicht konsultiert werden muss, da dies bereits jemand getan hat, festgestellt hat, dass der Clog xmaxabgebrochen wurde, und ein "Hinweisbit" gesetzt wurde, damit zukünftige Prozesse den Clog für diese Zeile nicht erneut besuchen müssen.

Welche Flags (oder Kombinationen von Flags) weisen das System an, den Clog zu besuchen?

Wenn es kein heap_xmin_committedoder gibt heap_xmin_invalid, müssen Sie den Clog besuchen, um zu sehen, wie die Disposition von xmin war. Wenn die Transaktion noch ausgeführt wird, ist die Zeile für Sie nicht sichtbar und Sie können keine Flags setzen. Wenn die Transaktion festgeschrieben oder zurückgesetzt wurde, legen Sie das heap_xmin_committedoder heap_xmin_invalidentsprechend fest (wenn dies zweckmäßig ist - es ist nicht obligatorisch), damit zukünftige Personen es nicht nachschlagen müssen.

Wenn xmingültig und festgeschrieben ist und wenn xmaxnicht Null ist und es kein heap_max_committedoder gibt heap_max_invalid, müssen Sie den Clog besuchen, um zu sehen, wie die Disposition dieser Transaktion war.

Gibt es eine Möglichkeit zu untersuchen, was sich im Clog befindet? In früheren Versionen von Postgres gibt es Hinweise auf Verstopfungsbeschädigungen und einen Hinweis darauf, dass eine gefälschte Datei manuell erstellt werden kann. Diese Information würde viel dabei helfen.

Mir ist keine benutzerfreundliche Vorgehensweise bekannt. Mit "od" können Sie die Clog-Dateien auf geeignete Weise sichern, um sie zu überprüfen, und mithilfe der in definierten Makros herausfinden, wo sie überprüft werden sollensrc/backend/access/transam/clog.c

Ich bin überrascht, dass es auf PGXN keine Erweiterungen gibt, die die Arbeit für Sie erledigen, aber ich konnte keine finden. Aber ich denke, es wäre nicht allzu nützlich, weil Sie dies wirklich tun müssen, während Ihr Server nicht läuft.

jjanes
quelle
4

Schauen Sie sich die Implementierung von HeapTupleSatisfiesMVCC () an : Die eigentliche clogPrüfung erfolgt in TransactionIdDidCommit () , wird jedoch nur aufgerufen, wenn der Transaktionsstatus nicht aus den Infomask-Bits ( Makro HeapTupleHeaderXminCommitted () und Freunden) abgeleitet werden kann.

Ich habe den Zugriff auf pg_clogFunktionen zurückverfolgt TransactionDidCommit()und TransactionDidAbort()dann nachgeschlagen, wo diese aufgerufen werden, und der einzige Ort im Code, der sich auf Ihre Frage bezieht, scheint sich darin zu befinden HeapTupleSatisfiesMVCC(). Aus dem Code dieser Funktion können Sie ersehen, dass die eigentliche Verstopfungssuche nur stattfinden kann, wenn für das Tupel die zugehörigen Infomask-Bits nicht gesetzt sind: Der Code beginnt mit der Überprüfung der Bits mit HeapTupleHeaderXminCommitted()et al. Und Clog Lookup findet nur statt, wenn die Bits nicht gesetzt sind.

Alex
quelle