W tym artykule chcę przedstawić obsługę ICU w PostgreSQL, nad którą pracowałem dla PostgreSQL w wersji 10, która pojawi się jeszcze w tym roku.
Sortowanie
Sortowanie jest ważną funkcjonalnością systemu bazodanowego. Po pierwsze, użytkownicy zazwyczaj chcą widzieć posortowane dane. Każdy wynik zapytania, który zawiera więcej niż jeden wiersz i jest przeznaczony do użytku przez użytkownika końcowego, prawdopodobnie będzie chciał zostać posortowany, aby zapewnić lepsze wrażenia użytkownika. Po drugie, wiele wewnętrznych funkcji systemu bazodanowego zależy od sortowania danych lub dostępności posortowanych danych. Indeksy B-drzewa są oczywistym przykładem. Indeksy BRIN posiadają wiedzę o porządku. Partycjonowanie zakresu musi porównywać wartości. Łączenia scalające zależą od posortowanych danych wejściowych. Pomysł, który jest wspólny dla tych różnych technik, polega na tym, że, mówiąc z grubsza, jeśli posortowałeś dane i wiesz, czego szukasz, znacznie przyspiesza to zlokalizowanie miejsca, w którym powinny zostać znalezione.
Sortowanie ma dwa ważne aspekty. Jednym z nich jest algorytm sortowania. Jest to standardowy temat w informatyce i przez lata wiele pracy włożono w PostgreSQL, aby udoskonalić różne algorytmy i metody sortowania, ale nie o tym będę pisał. Drugim jest decydowanie, w jakim porządku rzeczy powinny być, co nazywamy zestawieniem. W wielu przypadkach wybór ten jest oczywisty. 1 jest przed 2. FAŁSZ jest przed PRAWDĄ … cóż, ktoś po prostu arbitralnie zadecydował o tym. A zwykle pojawia się przed B. Ale jeśli chodzi o tekst w języku naturalnym, robi się ciekawie. Istnieje wiele różnych sposobów porządkowania tekstu, a rzeczywiste metody sortowania ciągów tekstowych są bardziej skomplikowane, niż mogłoby się wydawać. Różne języki preferują różne porządki sortowania, ale nawet w obrębie języka mogą występować różnice w różnych zastosowaniach. I trzeba się martwić o szczegóły, takie jak co zrobić z białymi znakami, interpunkcją, różnicami wielkości liter, znakami diakrytycznymi i tak dalej. Aby uzyskać więcej informacji na ten temat, zapoznaj się z algorytmem sortowania Unicode.
Zanim funkcja ICU została dodana, cała ta funkcjonalność ułatwiała biblioteka C w systemie operacyjnym. PostgreSQL po prostu przekazuje ciągi do strcmp()
, strcoll()
, i tym podobne i działa z wynikiem. Biblioteki C w różnych systemach operacyjnych implementują różne warianty sortowania i niuanse wymienione powyżej na różnych poziomach funkcjonalności i jakości, więc PostgreSQL może robić to, co potrafi Twój system operacyjny.
Zmienianie zestawień
Problemy zaczynają się, gdy system operacyjny kiedykolwiek będzie musiał zmienić sortowanie, które zapewnia. Dlaczego mieliby to robić? Możliwe, że poprzednie zestawienie było błędne i musiało zostać poprawione. Być może opublikowano nowy standard dla języka i zestawienie ma zostać zaktualizowane. Być może wewnętrzna reprezentacja danych sortowania i ciągów została zmieniona ze względu na wydajność lub dlatego, że konieczne było zaimplementowanie dodatkowej funkcjonalności. W przypadku wielu programów nie stanowi to problemu. Możesz po prostu zobaczyć nieco inaczej uporządkowane dane wyjściowe, jeśli w ogóle zauważysz różnicę. Jednak w przypadku systemu bazodanowego jest to poważny problem. Jak opisano powyżej, PostgreSQL przechowuje posortowane dane w indeksach i innych miejscach, a ich poprawność polega na kolejności sortowania. Jeśli kolejność sortowania nie jest poprawna, wyszukiwanie indeksu może nie znaleźć danych, które faktycznie tam są. Albo zapis do indeksu zapisze w innym miejscu. Lub dane są zapisywane lub odczytywane z niewłaściwej partycji. Może to prowadzić do błędnego zduplikowania danych lub pojawienia się utraty danych, ponieważ dane nie znajdują się tam, gdzie są poszukiwane. Innymi słowy, może to prowadzić do uszkodzenia danych i (pozornej) utraty danych.
Niestety do tej pory niewiele mogliśmy z tym zrobić. Systemy operacyjne aktualizują swoje zestawienia, kiedy tylko mają na to ochotę, być może w ramach aktualizacji pakietu biblioteki C. Nie ma sposobu, aby dowiedzieć się o tym w rozsądny sposób, niż być może poprzez szczegółowe zbadanie pakietów aktualizacji. A nawet wtedy, czy odrzucisz ważną aktualizację swojej biblioteki C, ponieważ zauważyłeś, że zmieniono sortowanie w niektórych lokalizacjach, których nie używasz? To była bardzo niewygodna sytuacja.
Wejdź na OIOM
Więc gdzie wchodzi ICU? ICU, International Components for Unicode, to biblioteka, która zapewnia funkcje internacjonalizacji i lokalizacji, w tym sortowanie. Pod tym względem jest to alternatywa dla korzystania z udogodnień standardowej biblioteki C. Fajną rzeczą jest to, że ICU wyraźnie zapewnia pewne gwarancje dotyczące stabilności zestawień:
- Sortowanie nie zostanie zmienione w niezgodny sposób w ramach aktualizacji mniejszej wersji.
- Porównanie ma wersję, którą można sprawdzić, a gdy porównanie zmienia się w niekompatybilny sposób, zmienia się wersja.
Dla użytkowników PostgreSQL oznacza to w praktyce:
- Rutynowe aktualizacje pakietów systemu operacyjnego nie wpływają na ważność posortowanych danych. Od
postgres
plik binarny jest powiązany z określoną główną wersjąlibicu
, rutynowe aktualizacje pakietów systemu operacyjnego nie kończą się napostgres
połączenie z nową główną wersjąlibicu
, o ile a) nie aktualizujesz pakietów PostgreSQL, lub b) pakiety PostgreSQL są nadal połączone z tą samą główną wersją ICU, co wcześniej. Osoby zajmujące się pakowaniem będą musiały być ostrożne, aby to właściwie utrzymać, ale w praktyce nie powinno to być zbyt problematyczne. - Gdy główne uaktualnienia pakietów i systemu operacyjnego zmieniają wersję zestawienia, mamy sposób, aby to wykryć i ostrzec użytkownika. W tej chwili po prostu ostrzegamy i oferujemy kilka wskazówek i narzędzi do naprawienia rzeczy, ale w przyszłości możemy to udoskonalić i zautomatyzować.
(Aby uczynić to bardziej wyraźnym dla pakujących:w stabilnej gałęzi systemu operacyjnego, nie powinieneś zmieniać głównej wersji ICU, z którą połączony jest dany zestaw pakietów PostgreSQL.)
Korzystanie z ICU
Aby móc z tego korzystać, PostgreSQL musi być zbudowany w sposób jawny z obsługą ICU. Budując ze źródeł, użyj ./configure --with-icu
wraz z innymi pożądanymi opcjami. Spodziewamy się, że większość głównych pakietów binarnych również będzie oferować to domyślnie. Po wykonaniu tej czynności sortowania oparte na ICU są oferowane wraz z sortowaniami opartymi na libc, które oferowały poprzednie wydania. (Więc budowanie z obsługą ICU nie usuwa obsługi sortowania libc; te dwie rzeczy istnieją razem.) Sprawdź dokumentację, aby uzyskać szczegółowe informacje na temat wyboru sortowania opartego na ICU w porównaniu do tego opartego na libc. Na przykład, jeśli wcześniej określiłeś
CREATE TABLE ... (... x text COLLATE "en_US" ...)
możesz teraz zrobić
CREATE TABLE ... (... x text COLLATE "en-x-icu" ...)
Powinno to dać mniej więcej takie samo widoczne dla użytkownika zachowanie jak poprzednio, z wyjątkiem tego, że Twoja baza danych będzie bardziej odporna na przyszłość, jeśli chodzi o aktualizację. (W systemie Linux/glibc porządek sortowania powinien być w większości taki sam, ale w niektórych szczegółach mogą występować niewielkie różnice. Jeśli jednak używasz systemu operacyjnego, którego biblioteka C w ogóle nie obsługuje sortowania Unicode, takiego jak macOS lub starsze wersje FreeBSD, to będzie to poważna zmiana — na lepsze.)
Obecnie obsługa ICU jest dostępna tylko dla jawnie określonych sortowań. Domyślne sortowanie w bazie danych jest zawsze zapewniane przez bibliotekę C. Rozwiązanie tego problemu to przyszły projekt.
Jeśli aktualizujesz taką bazę danych przez pg_upgrade
na przykład do nowej instalacji PostgreSQL, która jest połączona z nowszą główną wersją ICU, która zmieniła wersję sortowania tego sortowania, którego używasz, wtedy otrzymasz ostrzeżenie i będziesz musiał naprawić na przykład wszystkie indeksy zależne od porównanie. Instrukcje dotyczące tego znajdują się również w dokumentacji.
Klawisze skrócone
Tak więc ta zmiana zapewni kilka bardzo ważnych ulepszeń dla długoterminowej niezawodności systemu bazy danych. Ale ICU to także ulepszenie w stosunku do biblioteki systemu C w innych obszarach.
Na przykład drzewa PostgreSQL B mogą przechowywać tak zwane skrócone klucze, aby poprawić wydajność i pamięć. Dla typów danych typu ciąg tekstowy, ze standardową biblioteką C, obliczylibyśmy te skrócone klucze za pomocą strxfrm()
funkcjonować. Jednak dowiedzieliśmy się, że wiele bibliotek C zawiera różne błędy i niewłaściwe zachowania, które sprawiają, że to podejście nie jest niezawodne. Tak więc optymalizacja kluczy skróconych jest obecnie wyłączona dla typów danych typu string. Dzięki ICU możemy używać równoważnych wywołań API i obliczać skrócone klucze w sposób, który naszym zdaniem jest niezawodny i stabilny. Tak więc możliwe są również ulepszenia wydajności dzięki temu posunięciu.
Więcej zestawień
Oprócz tych wewnętrznych ulepszeń w zakresie niezawodności i wydajności, istnieje również kilka nowych funkcji skierowanych do użytkownika.
W przypadku niektórych języków w praktyce może mieć znaczenie więcej niż jeden porządek sortowania. (Może to ułatwić rozpoczęcie pracy). Jednym z przykładów jest to, że w przypadku języka niemieckiego istnieje standardowa kolejność sortowania używana do większości celów oraz kolejność sortowania „książki telefonicznej”, która jest używana w przypadku list nazwisk. Standardowa biblioteka C udostępnia tylko jeden z tych wariantów (prawdopodobnie pierwszy). Ale jeśli chcesz napisać aplikację, która prawidłowo sortuje, powiedzmy, zarówno nazwy produktów, jak i nazwy klientów, musisz mieć możliwość korzystania z obu.
Na przykład przykład z niemieckiej Wikipedii można teraz odtworzyć za 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
(W glibc, COLLATE "de_DE"
i COLLATE "de_AT"
rzeczywiście zwrócić pierwsze zamówienie.)
Ciekawym sposobem na połączenie kilku funkcji może być użycie domen do modelowania wyżej wymienionej różnicy między nazwami produktów a nazwami klientów:
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";
(To tylko przykład. Oczywiście możesz również dołączyć te COLLATE
klauzul do definicji kolumn bezpośrednio lub użyj ich w zapytaniach.)
Jeszcze więcej zestawień
Wreszcie, i wyraźnie na to czekał świat, istnieje teraz sposób na prawidłowe sortowanie emotikonów. Jest to niezbędne, aby upewnić się, że wszystkie twarze kota są w odpowiedniej kolejności. Porównaj
=# SELECT chr(x) FROM generate_series(x'1F634'::int, x'1F644'::int) AS _(x) ORDER BY chr(x) COLLATE "und-x-icu"; chr ----- 😴 😵 😶 😷 😸 😹 😺 😻 😼 😽 😾 😿 🙀 🙁 🙂 🙃 🙄
z
=# 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 ----- 🙂 🙃 😶 🙄 😴 😷 😵 🙁 😺 😸 😹 😻 😼 😽 🙀 😿 😾
Tak, w rzeczywistości jest to standard.
Więcej w przyszłości
To dopiero początek. ICU oferuje wiele funkcji w tym obszarze, których jeszcze nie udostępniamy za pośrednictwem PostgreSQL. Dostępne są opcje sortowania bez uwzględniania wielkości liter, sortowania bez uwzględniania akcentów i całkowitego dostosowywania sortowania. Poszukaj ich w przyszłych wydaniach PostgreSQL.