Das Symptom
Nach einem Betriebssystem-Update oder dem Wechsel auf ein neueres Container-Abbild
läuft die Datenbank scheinbar normal. Dann häufen sich merkwürdige Meldungen aus der
Fachabteilung: Ein Kunde ist über die Suche nicht auffindbar, obwohl sein Datensatz
existiert. Eine Abfrage mit WHERE name = 'Müller' liefert nichts, WHERE name LIKE 'Mül%' dagegen schon. Eine UNIQUE-Bedingung lässt plötzlich zwei gleiche Werte zu.
Im Serverprotokoll steht dazu eine Warnung über einen Versions-Konflikt bei einer
Collation: Sie sei in der Datenbank mit einer Version angelegt worden, das
Betriebssystem liefere eine andere. Dazu der Hinweis, alle betroffenen Objekte neu
aufzubauen und anschließend ALTER COLLATION ... REFRESH VERSION auszuführen.
Die „Layer 8“-Wahrheit
PostgreSQL sortiert Text nicht selbst. Es fragt die Sortierregeln beim
Betriebssystem ab — bei der C-Bibliothek oder bei ICU. Diese Regeln sind kein
Naturgesetz, sondern gepflegte Software, die sich mit jeder neuen Version ändern
darf. Und genau daran hängt jeder B-Baum-Index über einer Textspalte: Er ist eine
sortierte Struktur, aufgebaut nach der Reihenfolge, die zum Zeitpunkt des Aufbaus
galt. Ändert sich die Reihenfolge, ist der Baum nicht mehr sortiert. Die Suche
steigt an der falschen Stelle ab, findet nichts und meldet zufrieden: keine
Treffer.
Das Tückische ist die Stille. Es gibt keinen Absturz, keinen Fehler in der
Anwendung, keinen Eintrag im Fehlerprotokoll der Fachabteilung. Die Datenbank
antwortet schnell und selbstbewusst — nur falsch. Bei einer UNIQUE-Bedingung
bedeutet dasselbe Problem, dass eine Doppelung eingefügt werden darf, weil der
Index den bereits vorhandenen Wert nicht findet. Diese Dubletten bleiben auch nach
der Reparatur bestehen; das Neuindizieren deckt sie lediglich auf, indem es
scheitert.
Der verbreitetste Fehler bei der Behandlung ist die Reihenfolge. Die Warnung nervt,
ALTER COLLATION ... REFRESH VERSION macht sie weg, fertig. Das Handbuch weist
ausdrücklich darauf hin, dass dabei nicht geprüft wird, ob alle betroffenen Objekte
korrekt neu aufgebaut wurden. Das Auffrischen repariert also nichts, es beendet nur
die Beschwerde. Erst neu indizieren, dann auffrischen — in dieser Reihenfolge und in
keiner anderen.
Die echte, verifizierte Lösung
1000 % faktenbasiert. Jeder Befehl ist real, jeder Pfad existiert. Schritt für Schritt:
1.Die betroffenen Objekte auflisten
Das Handbuch liefert für diesen Zweck eine fertige Abfrage. Sie vergleicht die in der Datenbank vermerkte Collation-Version mit der, die das System gerade tatsächlich bereitstellt, und benennt jedes davon abhängige Objekt. Diese Liste ist der Arbeitsauftrag — nicht das Bauchgefühl, welche Tabellen wohl wichtig sind.
SELECT pg_describe_object(refclassid, refobjid, refobjsubid) AS "Collation",
pg_describe_object(classid, objid, objsubid) AS "Object"
FROM pg_depend d JOIN pg_collation c
ON refclassid = 'pg_collation'::regclass AND refobjid = c.oid
WHERE c.collversion <> pg_collation_actual_version(c.oid)
ORDER BY 1, 2;
2.Zuerst neu indizieren, nicht auffrischen
Der vom Handbuch genannte Weg beginnt mit dem Neuaufbau aller abhängigen Objekte, zum Beispiel per REINDEX. Erst danach darf die Version aufgefrischt werden. Wer die Reihenfolge tauscht, verliert die einzige Anzeige, die ihn noch auf das Problem hinweist. REINDEX sperrt betroffene Tabellen; die Variante CONCURRENTLY dauert länger, hält den Betrieb dafür am Laufen.
REINDEX DATABASE CONCURRENTLY meinedb;
3.Eindeutigkeit nachträglich prüfen
Ein beschädigter Index über einer UNIQUE-Bedingung kann Doppelungen durchgelassen haben, solange er kaputt war. Beim Neuaufbau fliegt das auf: Tritt beim Neuindizieren ein Problem auf, etwa eine Verletzung der Eindeutigkeit, schlägt REINDEX laut Handbuch fehl und hinterlässt zusätzlich zum bestehenden Index einen als ungültig markierten neuen. Der wird bei Abfragen ignoriert, kostet aber weiterhin Aufwand bei jeder Änderung — solche Reste tragen die Endung _ccnew beziehungsweise _ccold und müssen vor dem nächsten Versuch von Hand mit DROP INDEX entfernt werden. Die Dubletten selbst sind fachlich zu bereinigen. Genau deshalb gehört dieser Schritt in ein Wartungsfenster und nicht in die Mittagspause.
4.Erst jetzt die Collation-Version auffrischen
Sind alle abhängigen Objekte neu aufgebaut, wird der Systemkatalog auf die aktuelle Version gesetzt, und die Warnung verschwindet. Der Befehl steht so im Hinweis der Warnmeldung selbst. Der Name der Collation ist der aus der Meldung oder aus der Abfrage im ersten Schritt.
ALTER COLLATION pg_catalog."de-DE-x-icu" REFRESH VERSION;
5.Die Wiederholung beim nächsten Update einplanen
Das Handbuch stellt fest, dass so eine Änderung generell vermieden werden sollte, aber in legitimen Fällen vorkommt — bei einer neuen Hauptversion des Betriebssystems oder bei pg_upgrade auf Programmdateien mit neuerer ICU-Version. Daraus folgt eine Betriebsregel: Ein Distributionswechsel unter einem PostgreSQL-Server ist kein Routine-Update, sondern ein Vorgang mit anschließendem Neuindizieren. Wer das einplant, hat einen langen Abend. Wer es nicht einplant, hat wochenlang stille Falschauskünfte.