V tomto článku chci představit podporu ICU v PostgreSQL, na které jsem pracoval pro PostgreSQL verze 10, která se objeví později v tomto roce.
Řazení
Třídění je důležitou funkcí databázového systému. Za prvé, uživatelé obecně chtějí vidět data seřazená. Jakýkoli výsledek dotazu, který obsahuje více než jeden řádek a je určen pro spotřebu koncovým uživatelem, bude pravděpodobně chtít být seřazen, jen kvůli lepší uživatelské zkušenosti. Zadruhé, mnoho vnitřních funkcí databázového systému závisí na třídění dat nebo na tom, zda má tříděná data k dispozici. Indexy B-stromu jsou zřejmým příkladem. Indexy BRIN mají znalost řádu. Rozdělení rozsahu musí porovnávat hodnoty. Sloučení spojení závisí na seřazeném vstupu. Myšlenka, která je pro tyto různé techniky společná, je, že zhruba řečeno, pokud máte roztříděná data a víte, co hledáte, je mnohem rychlejší najít místo, kde by měla být nalezena.
Řazení má dva důležité aspekty. Jedním z nich je třídicí algoritmus. Toto je standardní téma v informatice a v PostgreSQL bylo v průběhu let vynaloženo mnoho práce na vylepšení různých třídicích algoritmů a metod, ale o tom nebudu psát. Druhým je rozhodování, v jakém pořadí by věci měly být, čemuž říkáme řazení. V mnoha případech je tato volba jasná. 1 je před 2. NEPRAVDA předchází PRAVDA … no, někdo to svévolně rozhodl. A obvykle přichází před B. Ale pokud jde o text v přirozeném jazyce, věci začínají být zajímavé. Existuje mnoho různých způsobů řazení textu a skutečné způsoby řazení textových řetězců jsou složitější, než by se mohlo zdát. Různé jazyky preferují různé pořadí řazení, ale i v rámci jazyka mohou existovat varianty pro různé aplikace. A jsou zde detaily, o které je třeba se starat, například co dělat s mezerami, interpunkcí, rozdíly mezi malými a velkými písmeny, diakritickými znaménky a tak dále. Podívejte se na Unicode Collation Algorithm pro více informací.
Před zavedením funkce ICU byla tato funkce umožněna knihovnou C v operačním systému. PostgreSQL v podstatě pouze předává řetězce do strcmp()
, strcoll()
, a podobně a pracuje s výsledkem. Knihovny C v různých operačních systémech implementují různé varianty řazení a nuance uvedené výše na různé úrovně funkčnosti a kvality, takže PostgreSQL dokáže to, co váš operační systém.
Změna řazení
Problémy začínají, pokud operační systém někdy potřebuje změnit řazení, které poskytuje. Proč by to chtěli dělat? Je možné, že předchozí řazení bylo chybné a muselo být opraveno. Možná byl zveřejněn nový standard pro jazyk a kvůli tomu je třeba aktualizovat řazení. Možná byla interní reprezentace dat porovnávání a řetězců změněna z důvodu výkonu nebo proto, že bylo nutné implementovat další funkce. U mnoha programů to není problém. Můžete vidět jen trochu jinak uspořádaný výstup, pokud si rozdíl vůbec všimnete. Pro databázový systém je to však zásadní problém. Jak je popsáno výše, PostgreSQL ukládá seřazená data do indexů a na jiná místa a spoléhá na správné pořadí řazení. Pokud pořadí řazení není správné, vyhledávání indexu nemusí najít data, která tam skutečně jsou. Nebo zápis do indexu zapíše na jiné místo. Nebo jsou data zapsána nebo čtena z nesprávného oddílu. To může vést k chybně duplicitním datům nebo ke ztrátě dat, protože data nejsou tam, kde jsou hledána. Jinými slovy, může to vést k poškození dat a (zdánlivé) ztrátě dat.
Bohužel se s tím zatím moc dělat nedalo. Operační systémy aktualizují své řazení, kdykoli se jim zachce, možná jako součást aktualizace jejich balíčku knihovny C. Nejde to nijak rozumně zjistit, nebo třeba podrobnou kontrolou aktualizačních balíčků. A dokonce i potom odmítnete důležitou aktualizaci vaší knihovny C, protože jste si všimli, že se řazení v některém národním prostředí, které nepoužíváte, změnilo? Byla to velmi nepříjemná situace.
Zadejte JIP
Takže, kde je JIP? ICU, International Components for Unicode, je knihovna, která poskytuje internacionalizační a lokalizační zařízení, včetně řazení. Takže v tomto ohledu je to alternativa k používání zařízení ve standardní knihovně C. Pěkné je, že JIP výslovně poskytuje určité záruky stability porovnávání:
- Pořadí nebude změněno nekompatibilním způsobem jako součást menší aktualizace vydání.
- Řazení má verzi, kterou lze zkontrolovat, a když se řazení změní nekompatibilním způsobem, změní se verze.
Pro uživatele PostgreSQL to bude v praxi znamenat:
- Rutinní aktualizace balíčků operačního systému nebudou narušovat platnost setříděných dat. Od
postgres
binární je spojeno s konkrétní hlavní verzílibicu
, rutinní aktualizace balíčků operačního systému neskončí spostgres
propojení s novou hlavní verzílibicu
, pokud a) neaktualizujete balíčky PostgreSQL nebo b) jsou balíčky PostgreSQL stále propojeny se stejnou hlavní verzí ICU jako dříve. Balíči budou muset být opatrní, aby to správně udržovali, ale v praxi by to nemělo být příliš problematické. - Když velké aktualizace balíčků a operačního systému změní verzi porovnávání, máme způsob, jak to zjistit a varovat uživatele. Právě teď jen varujeme a nabízíme nějaké pokyny a nástroje k nápravě věcí, ale v budoucnu to možná ještě vylepšíme a zautomatizujeme.
(Aby to bylo pro baliče jasnější:Ve stabilní větvi vašeho operačního systému byste neměli měnit hlavní verzi ICU, se kterou je daná sada balíčků PostgreSQL spojena.)
Použití JIP
Aby bylo možné toto použít, musí být PostgreSQL sestaven explicitně s podporou ICU. Při sestavování ze zdroje použijte ./configure --with-icu
spolu s dalšími požadovanými možnostmi. Očekáváme, že většina velkých binárních balíčků to bude standardně nabízet také. Když je toto provedeno, jsou nabízeny porovnávání založené na ICU spolu s porovnáváním založeným na libc, které nabízely předchozí verze. (Vytvoření s podporou ICU tedy neodstraní podporu porovnávání libc; obě existují společně.) Podrobnosti o tom, jak vybrat porovnávání založené na jednotce intenzivní péče oproti porovnávání založeném na libc, najdete v dokumentaci. Pokud jste například dříve zadali
CREATE TABLE ... (... x text COLLATE "en_US" ...)
teď můžete udělat
CREATE TABLE ... (... x text COLLATE "en-x-icu" ...)
To by vám mělo poskytnout zhruba stejné chování viditelné pro uživatele jako dříve, kromě toho, že vaše databáze bude odolnější vůči budoucnosti, pokud jde o upgrade. (Na Linuxu/glibc by pořadí řazení mělo být většinou stejné, ale v některých detailech mohou existovat malé rozdíly. Pokud však používáte operační systém, jehož knihovna C vůbec nepodporuje řazení Unicode, jako je macOS nebo starší verze FreeBSD, pak to bude velká změna — k lepšímu.)
V současné době je podpora JIP k dispozici pouze pro výslovně specifikované porovnávání. Výchozí řazení v databázi stále poskytuje knihovna C. Řešení tohoto problému je projekt budoucnosti.
Pokud upgradujete takovou databázi pomocí pg_upgrade
například na novou instalaci PostgreSQL, která je propojena s novější hlavní verzí ICU, která změnila verzi řazení tohoto řazení, které používáte, pak dostanete varování a budete muset opravit například všechny indexy, které závisí na řazení. Pokyny k tomu jsou také v dokumentaci.
Zkrácené klávesy
Tato změna tedy poskytne některá velmi důležitá vylepšení pro dlouhodobou robustnost databázového systému. JIP je však také vylepšením knihovny systému C v jiných oblastech.
Například PostgreSQL B-stromy mohou ukládat takzvané zkrácené klíče pro zlepšení výkonu a úložiště. Pro datové typy textových řetězců bychom se standardní knihovnou C vypočítali tyto zkrácené klíče pomocí strxfrm()
funkce. Zjistili jsme však, že mnoho knihoven C má řadu chyb a chybného chování, které činí tento přístup nespolehlivým. Optimalizace zkrácených klíčů je tedy pro datové typy řetězců aktuálně zakázána. S ICU můžeme použít ekvivalentní volání API a vypočítat zkrácené klíče způsobem, který je podle nás spolehlivý a stabilní. Takže i tento krok přináší možná zlepšení výkonu.
Další řazení
Kromě těchto interních vylepšení robustnosti a výkonu jsou zde také některé nové funkce pro uživatele.
U některých jazyků může být v praxi relevantní více než jedno pořadí řazení. (To by vám mohlo pomoci začít.) Jedním příkladem je, že pro němčinu existuje standardní pořadí řazení, které se používá pro většinu účelů, a pořadí řazení podle „telefonního seznamu“, které se používá pro seznamy jmen. Standardní knihovna C poskytuje pouze jednu z těchto variant (pravděpodobně první). Ale pokud chcete napsat aplikaci, která správně třídí, řekněme, jak názvy produktů, tak jména zákazníků, musíte umět používat obojí.
Například příklad z německé Wikipedie lze nyní reprodukovat pomocí 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
(S glibc, COLLATE "de_DE"
a COLLATE "de_AT"
skutečně vrátit první objednávku.)
Jedním ze zajímavých způsobů, jak zkombinovat několik funkcí, může být použití domén k modelování výše uvedeného rozdílu mezi názvy produktů a jmény zákazníků:
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";
(Toto je pouze příklad. Samozřejmě můžete také připojit COLLATE
klauzule na definice sloupců přímo nebo je použijte v dotazech.)
Ještě více porovnávání
Konečně, a to je jasně to, na co svět čekal, nyní existuje způsob, jak správně třídit emotikony. To je nezbytné, abyste zajistili, že všechny obličeje vaší kočky budou ve správném pořadí. Porovnat
=# SELECT chr(x) FROM generate_series(x'1F634'::int, x'1F644'::int) AS _(x) ORDER BY chr(x) COLLATE "und-x-icu"; chr ----- 😴 😵 😶 😷 😸 😹 😺 😻 😼 😽 😾 😿 🙀 🙁 🙂 🙃 🙄
s
=# 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 ----- 🙂 🙃 😶 🙄 😴 😷 😵 🙁 😺 😸 😹 😻 😼 😽 🙀 😿 😾
Ano, ve skutečnosti na to existuje standard.
Další bude
To je jen začátek. ICU nabízí v této oblasti mnoho funkcí, které zatím prostřednictvím PostgreSQL nezveřejňujeme. Existují možnosti pro třídění bez rozlišování malých a velkých písmen, třídění bez zvýraznění a úplné přizpůsobení řazení. Hledejte je v budoucích vydáních PostgreSQL.