PostgreSQL
 sql >> Datenbank >  >> RDS >> PostgreSQL

Robustere Sortierungen mit ICU-Unterstützung in PostgreSQL 10

In diesem Artikel möchte ich die ICU-Unterstützung in PostgreSQL vorstellen, an der ich für PostgreSQL Version 10 gearbeitet habe, die später in diesem Jahr erscheinen soll.

Sortierung

Sortieren ist eine wichtige Funktionalität eines Datenbanksystems. Erstens möchten Benutzer im Allgemeinen Daten sortiert sehen. Jedes Abfrageergebnis, das mehr als eine Zeile enthält und für den Endbenutzerverbrauch bestimmt ist, möchte wahrscheinlich sortiert werden, nur um die Benutzererfahrung zu verbessern. Zweitens hängt ein Großteil der internen Funktionalität eines Datenbanksystems davon ab, Daten zu sortieren oder sortierte Daten verfügbar zu haben. B-Tree-Indizes sind ein offensichtliches Beispiel. BRIN-Indizes haben Ordnungswissen. Die Bereichspartitionierung muss Werte vergleichen. Merge-Joins hängen von der sortierten Eingabe ab. Die Idee, die diesen verschiedenen Techniken gemeinsam ist, ist grob gesagt, dass wenn Sie Daten sortiert haben und wissen, wonach Sie suchen, es viel schneller geht, den Ort zu finden, an dem sie gefunden werden sollten.

Es gibt zwei wichtige Aspekte beim Sortieren. Einer ist der Sortieralgorithmus. Dies ist ein Standardthema in der Informatik, und in PostgreSQL ist im Laufe der Jahre viel Arbeit geflossen, um die verschiedenen Sortieralgorithmen und -methoden zu verfeinern, aber darüber werde ich nicht schreiben. Der andere entscheidet, in welcher Reihenfolge die Dinge sein sollen, was wir Kollation nennen. In vielen Fällen ist diese Wahl offensichtlich. 1 kommt vor 2. FALSCH kommt vor WAHR … nun, irgendjemand hat das einfach willkürlich entschieden. A kommt normalerweise vor B. Aber wenn es um Text in natürlicher Sprache geht, wird es interessant. Es gibt viele verschiedene Möglichkeiten, Text zu ordnen, und die tatsächlichen Methoden zum Sortieren von Textzeichenfolgen sind komplizierter, als es den Anschein haben mag. Unterschiedliche Sprachen bevorzugen unterschiedliche Sortierreihenfolgen, aber selbst innerhalb einer Sprache kann es Variationen für unterschiedliche Anwendungen geben. Und es gibt Details, über die Sie sich Gedanken machen müssen, z. B. was mit Leerzeichen, Satzzeichen, Unterschieden in Groß- und Kleinschreibung, diakritischen Zeichen usw. zu tun ist. Sehen Sie sich den Unicode-Sortierungsalgorithmus an, um mehr darüber zu erfahren.

Bevor die ICU-Funktion eingeführt wurde, wurde all diese Funktionalität durch die C-Bibliothek im Betriebssystem ermöglicht. PostgreSQL übergibt im Grunde nur Strings an strcmp() , strcoll() , und dergleichen und arbeitet mit dem Ergebnis. Die C-Bibliotheken in den verschiedenen Betriebssystemen implementieren die verschiedenen Kollatierungsvarianten und Nuancen, die oben erwähnt wurden, auf unterschiedlichen Funktions- und Qualitätsebenen, sodass PostgreSQL das tun kann, was Ihr Betriebssystem kann.

Sortierungen ändern

Probleme treten auf, wenn das Betriebssystem jemals eine bereitgestellte Sortierung ändern muss. Warum sollten sie das tun wollen? Es könnte sein, dass die vorherige Sortierung falsch war und korrigiert werden musste. Vielleicht wurde ein neuer Standard für eine Sprache veröffentlicht und dafür soll die Kollation aktualisiert werden. Möglicherweise wurde die interne Darstellung von Kollatierungs- und Zeichenfolgendaten aus Leistungsgründen geändert oder weil zusätzliche Funktionen implementiert werden mussten. Bei vielen Programmen ist dies kein Problem. Möglicherweise sehen Sie nur eine etwas anders geordnete Ausgabe, wenn Sie überhaupt einen Unterschied bemerken. Für ein Datenbanksystem ist dies jedoch ein großes Problem. Wie oben beschrieben, speichert PostgreSQL sortierte Daten in Indizes und anderen Orten und verlässt sich darauf, dass die Sortierreihenfolge korrekt ist. Wenn die Sortierreihenfolge nicht korrekt ist, findet eine Indexsuche möglicherweise keine Daten, die tatsächlich vorhanden sind. Oder ein Schreiben in einen Index schreibt an einen anderen Ort. Oder Daten werden auf die falsche Partition geschrieben oder von dieser gelesen. Dies kann zu fälschlicherweise doppelten Daten oder dem Anschein von Datenverlust führen, da sich die Daten nicht dort befinden, wo sie gesucht werden. Mit anderen Worten, es kann zu Datenkorruption und (scheinbarem) Datenverlust kommen.

Leider konnten wir bisher nicht viel dagegen tun. Betriebssysteme aktualisieren ihre Sortierungen, wann immer sie Lust dazu haben, vielleicht als Teil eines Upgrades ihres C-Bibliothekspakets. Es gibt keine Möglichkeit, dies auf vernünftige Weise herauszufinden, oder vielleicht, indem Sie die Update-Pakete im Detail untersuchen. Und selbst dann, werden Sie ein wichtiges Update Ihrer C-Bibliothek ablehnen, weil Sie bemerkt haben, dass die Sortierung in einem Gebietsschema, das Sie nicht verwenden, geändert wurde? Es war eine sehr unangenehme Situation.

Intensivstation betreten

Wo also kommt die Intensivstation ins Spiel? ICU, International Components for Unicode, ist eine Bibliothek, die Internationalisierungs- und Lokalisierungsfunktionen, einschließlich Sortierung, bereitstellt. In dieser Hinsicht ist es also eine Alternative zur Verwendung der Möglichkeiten in der Standard-C-Bibliothek. Das Schöne ist, dass ICU explizit einige Garantien über die Stabilität von Sortierungen gibt:

  • Eine Sortierung wird im Rahmen eines Minor-Release-Updates nicht auf inkompatible Weise geändert.
  • Eine Sortierung hat eine Version, die überprüft werden kann, und wenn sich eine Sortierung auf inkompatible Weise ändert, ändert sich die Version.

Für Benutzer von PostgreSQL bedeutet dies in der Praxis:

  • Routineaktualisierungen von Betriebssystempaketen beeinträchtigen die Gültigkeit sortierter Daten nicht. Da ein postgres Binary ist mit einer bestimmten Hauptversion von libicu verknüpft , werden routinemäßige Aktualisierungen von Betriebssystempaketen nicht mit postgres enden wird mit einer neuen Hauptversion von libicu verknüpft , solange a) Sie die PostgreSQL-Pakete nicht aktualisieren oder b) die PostgreSQL-Pakete immer noch mit derselben Hauptversion von ICU wie zuvor verknüpft sind. Paketierer müssen darauf achten, dies ordnungsgemäß zu pflegen, aber das sollte in der Praxis nicht allzu problematisch sein.
  • Wenn größere Paket- und Betriebssystem-Upgrades die Version einer Sortierung ändern, haben wir eine Möglichkeit, dies zu erkennen und den Benutzer zu warnen. Im Moment warnen wir nur und bieten einige Richtlinien und Tools zur Behebung von Problemen an, aber in Zukunft werden wir dies möglicherweise weiter verfeinern und automatisieren.

(Um dies für Paketierer deutlicher zu machen:In einem stabilen Zweig Ihres Betriebssystems sollten Sie die ICU-Hauptversion, mit der ein bestimmter PostgreSQL-Paketsatz verknüpft ist, nicht ändern.)

Verwendung der Intensivstation

Um dies nutzen zu können, muss PostgreSQL explizit mit ICU-Unterstützung gebaut werden. Verwenden Sie beim Erstellen aus der Quelle ./configure --with-icu zusammen mit anderen gewünschten Optionen. Wir erwarten, dass die meisten großen Binärpakete dies ebenfalls standardmäßig anbieten. Wenn dies erledigt ist, werden ICU-basierte Sortierungen neben den libc-basierten Sortierungen angeboten, die frühere Versionen angeboten haben. (Das Erstellen mit ICU-Unterstützung entfernt also nicht die libc-Kollatierungsunterstützung; die beiden existieren zusammen.) Schlagen Sie in der Dokumentation nach, um zu erfahren, wie Sie eine ICU-basierte Kollatierung im Vergleich zu einer libc-basierten auswählen. Wenn Sie beispielsweise zuvor angegeben hatten

CREATE TABLE ... (... x text COLLATE "en_US" ...)

Sie könnten jetzt tun

CREATE TABLE ... (... x text COLLATE "en-x-icu" ...)

Dies sollte Ihnen ungefähr das gleiche für den Benutzer sichtbare Verhalten wie zuvor geben, außer dass Ihre Datenbank zukunftssicherer ist, wenn es um ein Upgrade geht. (Unter Linux/glibc sollte die Sortierreihenfolge größtenteils gleich sein, es kann jedoch in einigen Details zu kleinen Unterschieden kommen. Wenn Sie jedoch ein Betriebssystem verwenden, dessen C-Bibliothek Unicode-Sortierung überhaupt nicht unterstützt, wie z. B. macOS oder älteren Versionen von FreeBSD, dann wird dies eine große Änderung sein – zum Besseren.)

Derzeit ist die ICU-Unterstützung nur für explizit angegebene Sortierungen verfügbar. Die Standardsortierung in einer Datenbank wird immer noch von der C-Bibliothek bereitgestellt. Dies anzugehen ist ein zukünftiges Projekt.

Wenn Sie eine solche Datenbank durch pg_upgrade aktualisieren B. zu einer neuen PostgreSQL-Installation, die mit einer neueren Hauptversion von ICU verknüpft ist, die die Sortierungsversion dieser von Ihnen verwendeten Sortierung geändert hat, dann erhalten Sie eine Warnung und müssen beispielsweise alle Indizes reparieren, die davon abhängen Kollation. Anweisungen dazu finden Sie auch in der Dokumentation.

Kurzschlüssel

Diese Änderung wird also einige sehr wichtige Verbesserungen für die langfristige Robustheit eines Datenbanksystems bieten. Aber ICU ist auch in anderen Bereichen eine Verbesserung gegenüber der System-C-Bibliothek.

Beispielsweise können PostgreSQL-B-Bäume so genannte abgekürzte Schlüssel speichern, um die Leistung und Speicherung zu verbessern. Für Text-String-Datentypen würden wir mit der Standard-C-Bibliothek diese abgekürzten Schlüssel mit strxfrm() berechnen Funktion. Wir haben jedoch gelernt, dass viele C-Bibliotheken eine Vielzahl von Fehlern und Fehlverhalten aufweisen, die diesen Ansatz nicht zuverlässig machen. Daher ist die Abkürzungstastenoptimierung derzeit für Zeichenfolgendatentypen deaktiviert. Mit ICU können wir die entsprechenden API-Aufrufe verwenden und abgekürzte Schlüssel auf eine unserer Meinung nach zuverlässige und stabile Weise berechnen. Es gibt also auch mögliche Leistungsverbesserungen durch diesen Schritt.

Weitere Sortierungen

Abgesehen von diesen internen Verbesserungen der Robustheit und Leistung gibt es auch einige neue benutzerorientierte Funktionen.

Für einige Sprachen kann in der Praxis mehr als eine Sortierreihenfolge relevant sein. (Dies könnte Ihnen den Einstieg erleichtern.) Ein Beispiel ist, dass es für Deutsch eine Standard-Sortierreihenfolge gibt, die für die meisten Zwecke verwendet wird, und eine „Telefonbuch“-Sortierreihenfolge, die für Namenslisten verwendet wird. Die Standard-C-Bibliothek bietet nur eine dieser Varianten (wahrscheinlich die erste). Aber wenn Sie eine Anwendung schreiben möchten, die beispielsweise Produktnamen und Kundennamen richtig sortiert, müssen Sie beide verwenden können.

Beispielsweise kann das Beispiel aus der deutschen Wikipedia nun mit PostgreSQL nachgebaut werden:

CREATE TABLE names (name text);

INSERT INTO names
    VALUES ('Göbel'), ('Goethe'), ('Goldmann'), ('Göthe'), ('Götz');

=> SELECT name FROM names ORDER BY name COLLATE "de-u-co-standard-x-icu";
   name
----------
 Göbel
 Goethe
 Goldmann
 Göthe
 Götz

=> SELECT name FROM names ORDER BY name COLLATE "de-u-co-phonebk-x-icu";
   name
----------
 Göbel
 Goethe
 Göthe
 Götz
 Goldmann

=> SELECT name FROM names ORDER BY name COLLATE "de-AT-u-co-phonebk-x-icu";
   name
----------
 Goethe
 Goldmann
 Göbel
 Göthe
 Götz

(Mit glibc, COLLATE "de_DE" und COLLATE "de_AT" in der Tat die erste Bestellung zurücksenden.)

Eine interessante Möglichkeit, mehrere Merkmale zu kombinieren, könnte die Verwendung von Domains sein, um den oben erwähnten Unterschied zwischen Produktnamen und Kundennamen zu modellieren:

CREATE DOMAIN product_name AS text COLLATE "de-u-co-standard-x-icu";
CREATE DOMAIN person_name AS text COLLATE "de-u-co-phonebk-x-icu";

(Dies ist nur ein Beispiel. Natürlich können Sie diese COLLATE auch anhängen Klauseln direkt in Spaltendefinitionen einfügen oder in Abfragen verwenden.)

Noch mehr Kollektionen

Endlich, und darauf hat die Welt eindeutig gewartet, gibt es jetzt eine Möglichkeit, Emojis richtig zu sortieren. Dies ist wichtig, um sicherzustellen, dass alle Ihre Katzengesichter in der richtigen Reihenfolge sind. Vergleiche

=# SELECT chr(x) FROM generate_series(x'1F634'::int, x'1F644'::int) AS _(x)
       ORDER BY chr(x) COLLATE "und-x-icu";
 chr
-----
 😴
 😵
 😶
 😷
 😸
 😹
 😺
 😻
 😼
 😽
 😾
 😿
 🙀
 🙁
 🙂
 🙃
 🙄

mit

=# CREATE COLLATION "und-u-co-emoji-x-icu" (provider = icu, locale = 'und-u-co-emoji');
=# SELECT chr(x) FROM generate_series(x'1F634'::int, x'1F644'::int) AS _(x)
       ORDER BY chr(x) COLLATE "und-u-co-emoji-x-icu";
 chr
-----
 🙂
 🙃
 😶
 🙄
 😴
 😷
 😵
 🙁
 😺
 😸
 😹
 😻
 😼
 😽
 🙀
 😿
 😾

Ja, dazu gibt es tatsächlich einen Standard.

Weitere folgen

Dies ist erst der Anfang. ICU bietet viele Funktionen in diesem Bereich, die wir noch nicht über PostgreSQL verfügbar machen. Es gibt Optionen für die Sortierung ohne Berücksichtigung der Groß- und Kleinschreibung, die Sortierung ohne Berücksichtigung von Akzenten und die vollständige Anpassung einer Sortierung. Suchen Sie in zukünftigen PostgreSQL-Versionen danach.