sql >> Database teknologi >  >> RDS >> PostgreSQL

Mere robuste kollationer med ICU-understøttelse i PostgreSQL 10

I denne artikel vil jeg introducere ICU-understøttelsen i PostgreSQL, som jeg har arbejdet på til PostgreSQL version 10, for at blive vist senere på året.

Sortering

Sortering er en vigtig funktion i et databasesystem. For det første ønsker brugere generelt at se data sorteret. Ethvert forespørgselsresultat, der indeholder mere end én række og er beregnet til slutbrugerforbrug, vil sandsynligvis gerne sorteres, bare for en bedre brugeroplevelse. For det andet afhænger meget af den interne funktionalitet i et databasesystem af at sortere data eller have sorterede data til rådighed. B-træindekser er et oplagt eksempel. BRIN-indekser har viden om orden. Range partitionering skal sammenligne værdier. Merge joins afhænger af sorteret input. Idéen, der er fælles for disse forskellige teknikker, er, at groft sagt, hvis du har sorteret data, og du ved, hvad du leder efter, gør det det meget hurtigere at finde det sted, hvor det skal findes.

Der er to vigtige aspekter ved sortering. Den ene er sorteringsalgoritmen. Dette er et standardemne inden for datalogi, og der er gået meget arbejde i PostgreSQL gennem årene for at forfine de forskellige sorteringsalgoritmer og metoder, men det er ikke det, jeg vil skrive om. Den anden er at bestemme, i hvilken rækkefølge tingene skal være, hvilket er det, vi kalder kollation. I mange tilfælde er det valg oplagt. 1 kommer før 2. FALSK kommer før SAND … ja, nogen har bare vilkårligt besluttet det. A kommer normalt før B. Men når det kommer til tekst i naturligt sprog, bliver tingene interessante. Der er mange forskellige måder at bestille tekst på, og de faktiske metoder til at sammensætte tekststrenge er mere komplicerede, end det måske er tilsyneladende. Forskellige sprog foretrækker forskellige sorteringsrækkefølger, men selv inden for et sprog kan der være variationer for forskellige applikationer. Og der er detaljer at bekymre sig om, såsom hvad man skal gøre ved mellemrum, tegnsætning, forskelle mellem store og små bogstaver, diakritiske tegn og så videre. Slå op i Unicode Collation Algorithm for mere indsigt i dette.

Før ICU-funktionen blev begået, var al denne funktionalitet lettet af C-biblioteket i operativsystemet. PostgreSQL sender stort set bare strenge til strcmp() , strcoll() , og lignende og arbejder med resultatet. C-bibliotekerne i de forskellige operativsystemer implementerer de forskellige sorteringsvarianter og -nuancer nævnt ovenfor til forskellige niveauer af funktionalitet og kvalitet, så PostgreSQL kan gøre, hvad dit operativsystem kan.

Ændring af sorteringer

Problemer starter, hvis operativsystemet nogensinde har brug for at ændre en sortering, det giver. Hvorfor skulle de ønske at gøre det? Det kunne være, at den tidligere sammenstilling var forkert og skulle rettes. Måske er der udgivet en ny standard for et sprog, og sammenstillingen skal opdateres til det. Måske er den interne repræsentation af sorterings- og strengdata blevet ændret af ydeevnemæssige årsager, eller fordi det var nødvendigt at implementere yderligere funktionalitet. For mange programmer er dette ikke et problem. Du ser måske bare et lidt anderledes ordnet output, hvis du overhovedet bemærker en forskel. For et databasesystem er dette dog et stort problem. Som beskrevet ovenfor gemmer PostgreSQL sorterede data i indekser og andre steder og er afhængig af, at sorteringsrækkefølgen er korrekt. Hvis sorteringsrækkefølgen ikke er korrekt, finder et indeksopslag muligvis ikke data, der rent faktisk er der. Eller en skrivning til et indeks vil skrive til et andet sted. Eller data skrives til eller læses fra den forkerte partition. Dette kan føre til fejlagtigt duplikerede data eller tilsyneladende tab af data, fordi data ikke er der, hvor de ledte efter. Med andre ord kan det føre til datakorruption og (tilsyneladende) datatab.

Desværre var der ikke meget vi kunne gøre ved det indtil videre. Operativsystemer opdaterer deres samlinger, når de har lyst, måske som en del af en opgradering til deres C-bibliotekspakke. Der er ingen måde at finde ud af om dette på en rimelig måde, eller end måske ved at inspicere opdateringspakkerne i detaljer. Og selv da, vil du afvise en vigtig opdatering af dit C-bibliotek, fordi du har bemærket, at sorteringen i en lokalitet, du ikke bruger, blev ændret? Det var en meget ubehagelig situation.

Gå ind i ICU

Så hvor kommer ICU ind? ICU, International Components for Unicode, er et bibliotek, der leverer internationaliserings- og lokaliseringsfaciliteter, inklusive kollation. Så i den henseende er det et alternativ til at bruge faciliteterne i standard C-biblioteket. Det gode er, at ICU eksplicit giver nogle garantier om stabiliteten af ​​kollationer:

  • En sortering vil ikke blive ændret på en inkompatibel måde som en del af en mindre udgivelsesopdatering.
  • En sortering har en version, som kan inspiceres, og når en sortering ændres på en inkompatibel måde, ændres versionen.

For brugere af PostgreSQL vil dette i praksis betyde:

  • Rutinemæssige opdateringer af operativsystempakke vil ikke forstyrre gyldigheden af ​​sorterede data. Siden en postgres binær er knyttet til en bestemt større version af libicu , rutinemæssige opgraderinger af operativsystempakke vil ikke ende med postgres bliver knyttet til en ny større version af libicu , så længe a) du ikke opdaterer PostgreSQL-pakkerne, eller b) PostgreSQL-pakkerne stadig er knyttet til den samme større version af ICU som før. Pakkere skal være omhyggelige med at vedligeholde dette korrekt, men det burde ikke være for problematisk i praksis.
  • Når større pakke- og operativsystemopgraderinger ændrer versionen af ​​en sortering, har vi en måde at opdage det og advare brugeren. Lige nu advarer vi og tilbyder nogle retningslinjer og værktøjer til at rette op på tingene, men i fremtiden vil vi muligvis forfine og automatisere dette yderligere.

(For at gøre dette mere eksplicit for pakkere:I en stabil gren af ​​dit operativsystem bør du ikke ændre den større ICU-version, som et givet PostgreSQL-pakkesæt er forbundet med.)

Brug af ICU

For at kunne bruge dette, skal PostgreSQL bygges eksplicit med ICU-understøttelse. Når du bygger fra kilde, skal du bruge ./configure --with-icu sammen med andre ønskede muligheder. Vi forventer, at de fleste større binære pakker også tilbyder dette som standard. Når dette er gjort, tilbydes ICU-baserede sorteringer sammen med de libc-baserede sorteringer, som tidligere udgivelser tilbød. (Så bygning med ICU-understøttelse fjerner ikke libc-sorteringsunderstøttelse; de ​​to eksisterer sammen.) Se dokumentationen for detaljer om, hvordan man vælger en ICU-baseret sortering versus en libc-baseret. For eksempel, hvis du tidligere havde angivet

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

du kan nu gøre

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

Dette skulle give dig nogenlunde den samme brugersynlige adfærd som før, bortset fra at din database vil være mere fremtidssikret, når det kommer til opgradering. (På Linux/glibc skal sorteringsrækkefølgen stort set være den samme, men der kan være små forskelle i nogle detaljer. Hvis du dog bruger et operativsystem, hvis C-bibliotek slet ikke understøtter Unicode-sortering, som f.eks. macOS eller ældre versioner af FreeBSD, så vil dette være en stor ændring - til det bedre.)

I øjeblikket er ICU-support kun tilgængelig for eksplicit specificerede sammenstillinger. Standardsorteringen i en database leveres stadig altid af C-biblioteket. At tage fat på dette er et fremtidigt projekt.

Hvis du opgraderer en sådan database med pg_upgrade for eksempel til en ny PostgreSQL-installation, der er forbundet med en nyere større version af ICU, der har ændret sorteringsversionen af ​​den sortering, du bruger, så får du en advarsel og bliver nødt til at rette op på for eksempel eventuelle indekser, der afhænger af sammenstilling. Instruktioner hertil findes også i dokumentationen.

Forkortede nøgler

Så denne ændring vil give nogle meget vigtige forbedringer for langsigtet robusthed af et databasesystem. Men ICU er også en forbedring i forhold til system C-biblioteket på andre områder.

For eksempel kan PostgreSQL B-træer gemme det, der kaldes forkortede nøgler for at forbedre ydeevne og lagring. For tekststrengdatatyper ville vi med standard C-biblioteket beregne disse forkortede nøgler ved hjælp af strxfrm() fungere. Vi har dog erfaret, at mange C-biblioteker har en række forskellige fejl og dårlig opførsel, der gør denne tilgang ikke pålidelig. Så optimering af forkortede nøgler er i øjeblikket deaktiveret for strengdatatyper. Med ICU kan vi bruge de tilsvarende API-kald og beregne forkortede nøgler på, hvad vi mener er en pålidelig og stabil måde. Så der er også mulige præstationsforbedringer fra dette træk.

Flere samlinger

Ud over disse interne forbedringer af robusthed og ydeevne er der også nogle nye brugervendte funktioner.

For nogle sprog kan mere end én sorteringsrækkefølge være relevant i praksis. (Dette kan få dig i gang.) Et eksempel er, at for tysk er der en standard sorteringsrækkefølge, der bruges til de fleste formål, og en "telefonbogs"-sorteringsrækkefølge, der bruges til lister med navne. Standard C-biblioteket giver kun en af ​​disse varianter (sandsynligvis den første). Men hvis du vil skrive en applikation, der korrekt sorterer f.eks. både produktnavne og kundenavne, skal du kunne bruge begge dele.

Eksempelvis kan eksemplet fra den tyske Wikipedia nu gengives med PostgreSQL:

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

(Med glibc, COLLATE "de_DE" og COLLATE "de_AT" faktisk returnere den første ordre.)

En interessant måde at kombinere flere funktioner på kan være at bruge domæner til at modellere ovennævnte forskel mellem produktnavne og kundenavne:

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";

(Dette er kun et eksempel. Du kan selvfølgelig også vedhæfte disse COLLATE klausuler til kolonnedefinitioner direkte eller brug dem i forespørgsler.)

Endnu flere samlinger

Endelig, og det er helt klart, hvad verden havde ventet på, er der nu en måde at sortere emojis på. Dette er vigtigt for at sikre, at alle dine katteansigter er i den rigtige rækkefølge. Sammenlign

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

med

=# 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, der er faktisk en standard om dette.

Mere på vej

Dette er blot begyndelsen. ICU tilbyder en masse funktionalitet på dette område, som vi ikke eksponerer gennem PostgreSQL endnu. Der er muligheder for sortering uden store og små bogstaver, accentufølsom sortering og fuldstændig tilpasning af en sortering. Se efter dem i fremtidige PostgreSQL-udgivelser.


  1. Forespørgselsoptimering i PostgreSQL. FORKLAR Grundlæggende – Del 2

  2. Hvad er databaseafhængigheder?

  3. PHP PDO udarbejdede erklæringer

  4. Hvad er den bedste måde at slette gamle rækker fra MySQL på en rullende basis?