In dit artikel wil ik de ICU-ondersteuning in PostgreSQL introduceren, waaraan ik heb gewerkt voor PostgreSQL versie 10, die later dit jaar verschijnt.
Sorteren
Sorteren is een belangrijke functionaliteit van een databasesysteem. Ten eerste willen gebruikers over het algemeen gegevens gesorteerd zien. Elk zoekresultaat dat meer dan één rij bevat en bestemd is voor gebruik door eindgebruikers, wil waarschijnlijk gesorteerd worden, alleen voor een betere gebruikerservaring. Ten tweede hangt veel van de interne functionaliteit van een databasesysteem af van het sorteren van gegevens of het beschikbaar hebben van gesorteerde gegevens. B-tree-indexen zijn een voor de hand liggend voorbeeld. BRIN-indexen hebben kennis van orde. Range-partitionering moet waarden vergelijken. Samenvoegingen zijn afhankelijk van de gesorteerde invoer. Het idee dat deze verschillende technieken gemeen hebben, is dat, ruwweg gezegd, als je gegevens hebt gesorteerd en je weet waarnaar je zoekt, het veel sneller is om de plaats te vinden waar het moet worden gevonden.
Er zijn twee belangrijke aspecten bij het sorteren. Een daarvan is het sorteeralgoritme. Dit is een standaardonderwerp in de informatica en er is in de loop der jaren veel werk verzet in PostgreSQL om de verschillende sorteeralgoritmen en -methoden te verfijnen, maar daar zal ik niet over schrijven. De andere is beslissen in welke volgorde dingen moeten zijn, wat we collatie noemen. Die keuze ligt in veel gevallen voor de hand. 1 komt voor 2. ONWAAR komt voor WAAR ... nou, iemand heeft dat zomaar willekeurig besloten. A komt meestal voor B. Maar als het gaat om tekst in natuurlijke taal, wordt het interessant. Er zijn veel verschillende manieren om tekst te ordenen, en de eigenlijke methoden om tekstreeksen te verzamelen zijn ingewikkelder dan het lijkt. Verschillende talen geven de voorkeur aan verschillende sorteervolgorden, maar zelfs binnen een taal kunnen er variaties zijn voor verschillende toepassingen. En er zijn details om je zorgen over te maken, zoals wat je moet doen met witruimte, interpunctie, verschillen in hoofdletters, diakritische tekens, enzovoort. Zoek het Unicode Collation Algorithm op voor meer inzicht hierin.
Voordat de ICU-functie werd vastgelegd, werd al deze functionaliteit gefaciliteerd door de C-bibliotheek in het besturingssysteem. PostgreSQL geeft in feite alleen strings door aan strcmp()
, strcoll()
, en dergelijke en werkt met het resultaat. De C-bibliotheken in de verschillende besturingssystemen implementeren de verschillende sorteervarianten en nuances die hierboven zijn genoemd op verschillende niveaus van functionaliteit en kwaliteit, zodat PostgreSQL kan doen wat uw besturingssysteem kan doen.
Collaties wijzigen
Problemen beginnen als het besturingssysteem ooit een sortering moet wijzigen. Waarom zouden ze dat willen doen? Het kan zijn dat de vorige sortering verkeerd was en gerepareerd moest worden. Misschien is er een nieuwe standaard voor een taal gepubliceerd en moet de verzameling daarvoor worden bijgewerkt. Misschien is de interne weergave van sorteer- en tekenreeksgegevens gewijzigd om prestatieredenen of omdat het nodig was om extra functionaliteit te implementeren. Voor veel programma's is dit geen probleem. U ziet misschien een iets anders geordende uitvoer, als u al een verschil merkt. Voor een databasesysteem is dit echter een groot probleem. Zoals hierboven beschreven, slaat PostgreSQL gesorteerde gegevens op in indexen en andere plaatsen en vertrouwt erop dat de sorteervolgorde correct is. Als de sorteervolgorde niet correct is, kan een indexzoekopdracht geen gegevens vinden die er daadwerkelijk zijn. Of een schrijven naar een index zal naar een andere plaats schrijven. Of er worden gegevens geschreven naar of gelezen van de verkeerde partitie. Dit kan leiden tot foutieve dubbele gegevens of de schijn van gegevensverlies omdat de gegevens niet zijn waar ze worden gezocht. Met andere woorden, het kan leiden tot datacorruptie en (schijnbaar) dataverlies.
Helaas konden we er tot nu toe niet veel aan doen. Besturingssystemen werken hun verzamelingen bij wanneer ze daar zin in hebben, misschien als onderdeel van een upgrade naar hun C-bibliotheekpakket. Er is geen manier om hier op een redelijke manier achter te komen, of misschien door de updatepakketten in detail te inspecteren. En zelfs dan, wilt u een belangrijke update van uw C-bibliotheek afwijzen omdat u hebt opgemerkt dat de sortering in een landinstelling die u niet gebruikt, is gewijzigd? Het was een erg ongemakkelijke situatie.
Betreed ICU
Dus waar komt ICU binnen? ICU, International Components for Unicode, is een bibliotheek die internationaliserings- en lokalisatiefaciliteiten biedt, inclusief sortering. Wat dat betreft is het dus een alternatief voor het gebruik van de faciliteiten in de standaard C-bibliotheek. Het leuke is dat ICU expliciet enkele garanties geeft over de stabiliteit van sorteringen:
- Een sortering wordt niet op een incompatibele manier gewijzigd als onderdeel van een kleine release-update.
- Een sortering heeft een versie die kan worden geïnspecteerd, en wanneer een sortering op een incompatibele manier verandert, verandert de versie.
Voor gebruikers van PostgreSQL betekent dit in de praktijk:
- Routine updates van het besturingssysteempakket hebben geen invloed op de geldigheid van gesorteerde gegevens. Sinds een
postgres
binair is gekoppeld aan een bepaalde hoofdversie vanlibicu
, zullen routinematige upgrades van besturingssystemen niet eindigen metpostgres
gekoppeld worden aan een nieuwe hoofdversie vanlibicu
, zolang a) u de PostgreSQL-pakketten niet bijwerkt, of b) de PostgreSQL-pakketten nog steeds zijn gekoppeld aan dezelfde hoofdversie van ICU als voorheen. Verpakkers zullen voorzichtig moeten zijn om dit goed te onderhouden, maar dat zou in de praktijk niet al te problematisch moeten zijn. - Als grote pakket- en besturingssysteemupgrades de versie van een sortering veranderen, hebben we een manier om dat te detecteren en de gebruiker te waarschuwen. Op dit moment waarschuwen we alleen en bieden we enkele richtlijnen en hulpmiddelen om dingen op te lossen, maar in de toekomst kunnen we dit verder verfijnen en automatiseren.
(Om dit explicieter te maken voor verpakkers:in een stabiele tak van uw besturingssysteem moet u de belangrijkste ICU-versie waaraan een bepaalde PostgreSQL-pakketset is gekoppeld niet wijzigen.)
ICU gebruiken
Om dit te kunnen gebruiken, moet PostgreSQL expliciet worden gebouwd met ICU-ondersteuning. Gebruik bij het bouwen vanaf de bron ./configure --with-icu
samen met andere gewenste opties. We verwachten dat de meeste grote binaire pakketten dit ook standaard aanbieden. Wanneer dit is gebeurd, worden op ICU gebaseerde sorteringen aangeboden naast de op libc gebaseerde sorteringen die eerdere releases aanboden. (Dus bouwen met ICU-ondersteuning verwijdert de libc-collationeringsondersteuning niet; de twee bestaan samen.) Raadpleeg de documentatie voor details over het selecteren van een ICU-gebaseerde collatie versus een libc-gebaseerde collatie. Als u bijvoorbeeld eerder
CREATE TABLE ... (... x text COLLATE "en_US" ...)
je zou nu kunnen doen
CREATE TABLE ... (... x text COLLATE "en-x-icu" ...)
Dit zou u ongeveer hetzelfde voor de gebruiker zichtbare gedrag moeten geven als voorheen, behalve dat uw database meer toekomstbestendig zal zijn als het gaat om upgraden. (Op Linux/glibc zou de sorteervolgorde grotendeels hetzelfde moeten zijn, maar er kunnen kleine verschillen zijn in sommige details. Als u echter een besturingssysteem gebruikt waarvan de C-bibliotheek Unicode-sortering helemaal niet ondersteunt, zoals macOS of oudere versies van FreeBSD, dan zal dit een grote verandering zijn — ten goede.)
Momenteel is ICU-ondersteuning alleen beschikbaar voor expliciet gespecificeerde sorteringen. De standaardsortering in een database wordt nog steeds altijd geleverd door de C-bibliotheek. Dit aanpakken is een toekomstig project.
Als u een dergelijke database opwaardeert met pg_upgrade
bijvoorbeeld naar een nieuwe PostgreSQL-installatie die is gekoppeld aan een nieuwere hoofdversie van ICU die de sorteerversie van die sortering die u gebruikt heeft gewijzigd, dan krijgt u een waarschuwing en moet u bijvoorbeeld alle indexen herstellen die afhankelijk zijn van de sortering. Instructies hiervoor staan ook in de documentatie.
Verkorte toetsen
Deze wijziging zal dus een aantal zeer belangrijke verbeteringen opleveren voor de robuustheid van een databasesysteem op de lange termijn. Maar ICU is ook op andere gebieden een verbetering ten opzichte van de bibliotheek van systeem C.
PostgreSQL B-trees kunnen bijvoorbeeld verkorte sleutels opslaan om de prestaties en opslag te verbeteren. Voor gegevenstypen met tekstreeksen, met de standaard C-bibliotheek, zouden we deze verkorte sleutels berekenen met behulp van de strxfrm()
functie. We hebben echter geleerd dat veel C-bibliotheken een verscheidenheid aan bugs en wangedrag hebben die deze aanpak niet betrouwbaar maken. Dus de verkorte sleuteloptimalisatie is momenteel uitgeschakeld voor stringgegevenstypen. Met ICU kunnen we de equivalente API-aanroepen gebruiken en verkorte sleutels berekenen op een naar onze mening betrouwbare en stabiele manier. Er zijn dus ook mogelijke prestatieverbeteringen van deze zet.
Meer sorteringen
Afgezien van deze interne verbeteringen van robuustheid en prestaties, is er ook een aantal nieuwe gebruikersgerichte functionaliteit.
Voor sommige talen kan in de praktijk meer dan één sorteervolgorde relevant zijn. (Hiermee kunt u misschien aan de slag.) Een voorbeeld is dat er voor Duits een standaardsorteervolgorde is die voor de meeste doeleinden wordt gebruikt en een "telefoonboek"-sorteervolgorde die wordt gebruikt voor namenlijsten. De standaard C-bibliotheek biedt slechts één van die varianten (waarschijnlijk de eerste). Maar als u een toepassing wilt schrijven die bijvoorbeeld zowel productnamen als klantnamen op de juiste manier sorteert, moet u beide kunnen gebruiken.
Het voorbeeld van de Duitse Wikipedia kan nu bijvoorbeeld worden gereproduceerd met 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
(Met glibc, COLLATE "de_DE"
en COLLATE "de_AT"
retourneer inderdaad de eerste bestelling.)
Een interessante manier om verschillende functies te combineren is om domeinen te gebruiken om het bovengenoemde verschil tussen productnamen en klantnamen te modelleren:
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";
(Dit is slechts een voorbeeld. U kunt deze COLLATE
natuurlijk ook bijvoegen clausules rechtstreeks naar kolomdefinities of gebruik ze in zoekopdrachten.)
Nog meer sorteringen
Eindelijk, en dit is duidelijk waar de wereld op had gewacht, is er nu een manier om emoji's goed te sorteren. Dit is essentieel om ervoor te zorgen dat al uw kattengezichten in de juiste volgorde staan. Vergelijk
=# SELECT chr(x) FROM generate_series(x'1F634'::int, x'1F644'::int) AS _(x) ORDER BY chr(x) COLLATE "und-x-icu"; chr ----- 😴 😵 😶 😷 😸 😹 😺 😻 😼 😽 😾 😿 🙀 🙁 🙂 🙃 🙄
met
=# 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, hier is eigenlijk een norm over.
Er komt nog meer
Dit is nog maar het begin. ICU biedt veel functionaliteit op dit gebied die we nog niet via PostgreSQL blootleggen. Er zijn opties voor hoofdletterongevoelig sorteren, accentongevoelig sorteren en het volledig aanpassen van een sortering. Zoek die in toekomstige PostgreSQL-releases.