PostgreSQL
 sql >> Base de données >  >> RDS >> PostgreSQL

Classements plus robustes avec prise en charge ICU dans PostgreSQL 10

Dans cet article, je souhaite présenter le support ICU dans PostgreSQL, sur lequel j'ai travaillé pour PostgreSQL version 10, qui paraîtra plus tard cette année.

Trier

Le tri est une fonctionnalité importante d'un système de base de données. Premièrement, les utilisateurs veulent généralement voir les données triées. Tout résultat de requête contenant plus d'une ligne et destiné à la consommation de l'utilisateur final voudra probablement être trié, juste pour une meilleure expérience utilisateur. Deuxièmement, une grande partie des fonctionnalités internes d'un système de base de données dépend du tri des données ou de la disponibilité de données triées. Les index B-tree en sont un exemple évident. Les index BRIN ont connaissance de l'ordre. Le partitionnement de plage doit comparer les valeurs. Les jointures de fusion dépendent de l'entrée triée. L'idée commune à ces différentes techniques est que, grosso modo, si vous avez des données triées et que vous savez ce que vous cherchez, cela permet de localiser beaucoup plus rapidement l'endroit où il faut les trouver.

Il y a deux aspects importants au tri. L'un est l'algorithme de tri. C'est un sujet standard en informatique, et beaucoup de travail a été consacré à PostgreSQL au fil des ans pour affiner les différents algorithmes et méthodes de tri, mais ce n'est pas ce sur quoi je vais écrire. L'autre consiste à décider dans quel ordre les choses doivent être, c'est ce que nous appelons la collation. Dans de nombreux cas, ce choix est évident. 1 vient avant 2. FAUX vient avant VRAI… eh bien, quelqu'un vient de décider arbitrairement celui-là. A vient généralement avant B. Mais quand il s'agit de texte en langage naturel, les choses deviennent intéressantes. Il existe de nombreuses façons différentes de classer du texte, et les méthodes réelles pour assembler des chaînes de texte sont plus compliquées qu'il n'y paraît. Différentes langues préfèrent différents ordres de tri, mais même au sein d'une langue, il peut y avoir des variations pour différentes applications. Et il y a des détails dont il faut s'inquiéter, comme ce qu'il faut faire avec les espaces, la ponctuation, les différences de casse, les signes diacritiques, etc. Consultez l'algorithme de classement Unicode pour en savoir plus.

Avant que la fonctionnalité ICU ne soit validée, toutes ces fonctionnalités étaient facilitées par la bibliothèque C dans le système d'exploitation. PostgreSQL passe simplement des chaînes à strcmp() , strcoll() , etc. et travaille avec le résultat. Les bibliothèques C des différents systèmes d'exploitation implémentent les différentes variantes et nuances de classement mentionnées ci-dessus à différents niveaux de fonctionnalité et de qualité, afin que PostgreSQL puisse faire ce que votre système d'exploitation peut faire.

Modifier les classements

Les problèmes commencent si le système d'exploitation a besoin de modifier un classement qu'il fournit. Pourquoi voudraient-ils faire cela ? Il se peut que le classement précédent soit erroné et doive être corrigé. Peut-être qu'une nouvelle norme pour une langue a été publiée et que la collation doit être mise à jour pour cela. La représentation interne des données de classement et de chaîne a peut-être été modifiée pour des raisons de performances ou parce qu'il était nécessaire d'implémenter des fonctionnalités supplémentaires. Pour de nombreux programmes, ce n'est pas un problème. Vous pourriez juste voir une sortie ordonnée légèrement différemment, si vous remarquez une différence du tout. Pour un système de base de données, cependant, c'est un problème majeur. Comme décrit ci-dessus, PostgreSQL stocke les données triées dans des index et d'autres endroits et s'appuie sur l'ordre de tri pour être correct. Si l'ordre de tri n'est pas correct, une recherche d'index peut ne pas trouver les données qui s'y trouvent réellement. Ou une écriture dans un index écrira à un endroit différent. Ou les données sont écrites ou lues à partir de la mauvaise partition. Cela peut conduire à des données dupliquées par erreur ou à l'apparence d'une perte de données car les données ne sont pas là où elles sont recherchées. En d'autres termes, cela peut entraîner une corruption des données et une perte (apparente) de données.

Malheureusement, nous ne pouvions pas faire grand-chose jusqu'à présent. Les systèmes d'exploitation mettent à jour leurs classements chaque fois qu'ils en ont envie, peut-être dans le cadre d'une mise à niveau de leur package de bibliothèque C. Il n'y a aucun moyen de le savoir de manière raisonnable, ou peut-être en inspectant en détail les packages de mise à jour. Et même dans ce cas, refuserez-vous une mise à jour importante de votre bibliothèque C parce que vous avez remarqué que le classement dans certains paramètres régionaux que vous n'utilisez pas a été modifié ? C'était une situation très inconfortable.

Entrez aux soins intensifs

Alors, d'où vient l'ICU? ICU, International Components for Unicode, est une bibliothèque qui fournit des fonctionnalités d'internationalisation et de localisation, y compris le classement. Donc, à cet égard, c'est une alternative à l'utilisation des fonctionnalités de la bibliothèque C standard. La bonne chose est qu'ICU fournit explicitement des garanties sur la stabilité des classements :

  • Un classement ne sera pas modifié de manière incompatible dans le cadre d'une mise à jour de version mineure.
  • Un classement a une version, qui peut être inspectée, et lorsqu'un classement change de manière incompatible, la version change.

Pour les utilisateurs de PostgreSQL, cela signifiera en pratique :

  • Les mises à jour de routine des packages du système d'exploitation n'interfèrent pas avec la validité des données triées. Depuis un postgres le binaire est lié à une version majeure particulière de libicu , les mises à jour de routine des packages du système d'exploitation ne se termineront pas avec postgres être lié à une nouvelle version majeure de libicu , tant que a) vous ne mettez pas à jour les packages PostgreSQL, ou b) les packages PostgreSQL sont toujours liés à la même version majeure d'ICU qu'auparavant. Les conditionneurs devront faire attention à maintenir cela correctement, mais cela ne devrait pas être trop problématique dans la pratique.
  • Lorsque des mises à niveau majeures de package et de système d'exploitation modifient la version d'un classement, nous avons un moyen de le détecter et d'avertir l'utilisateur. Pour le moment, nous nous contentons d'avertir et de proposer des instructions et des outils pour résoudre les problèmes, mais à l'avenir, nous pourrions affiner et automatiser davantage cela.

(Pour rendre cela plus explicite pour les empaqueteurs :dans une branche stable de votre système d'exploitation, vous ne devez pas modifier la version majeure d'ICU à laquelle un ensemble de packages PostgreSQL donné est lié.)

Utilisation des soins intensifs

Pour pouvoir l'utiliser, PostgreSQL doit être construit explicitement avec le support ICU. Lors de la construction à partir de la source, utilisez ./configure --with-icu ainsi que d'autres options souhaitées. Nous nous attendons à ce que la plupart des principaux packages binaires offrent également cela par défaut. Lorsque cela est fait, les classements basés sur ICU sont proposés parallèlement aux classements basés sur libc proposés par les versions précédentes. (Ainsi, la construction avec le support ICU ne supprime pas le support du classement libc; les deux existent ensemble.) Consultez la documentation pour plus de détails sur la façon de sélectionner un classement basé sur ICU par rapport à un classement basé sur libc. Par exemple, si vous aviez précédemment spécifié

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

vous pourriez maintenant faire

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

Cela devrait vous donner à peu près le même comportement visible par l'utilisateur qu'auparavant, sauf que votre base de données sera plus évolutive en ce qui concerne la mise à niveau. (Sous Linux/glibc, l'ordre de tri devrait être essentiellement le même, mais il pourrait y avoir de petites différences dans certains détails. Si, toutefois, vous utilisez un système d'exploitation dont la bibliothèque C ne prend pas du tout en charge le classement Unicode, comme macOS ou anciennes versions de FreeBSD, alors ce sera un changement majeur - pour le mieux.)

Actuellement, la prise en charge d'ICU n'est disponible que pour les classements explicitement spécifiés. Le classement par défaut dans une base de données est toujours fourni par la bibliothèque C. Résoudre ce problème est un projet futur.

Si vous mettez à jour une telle base de données par pg_upgrade par exemple à une nouvelle installation PostgreSQL qui est liée à une version majeure plus récente d'ICU qui a changé la version de classement de ce classement que vous utilisez, vous recevrez alors un avertissement et devrez corriger par exemple tous les index qui dépendent de la collation. Les instructions pour cela se trouvent également dans la documentation.

Touches abrégées

Ce changement apportera donc des améliorations très importantes pour la robustesse à long terme d'un système de base de données. Mais ICU est également une amélioration par rapport à la bibliothèque système C dans d'autres domaines.

Par exemple, les arbres B PostgreSQL peuvent stocker ce qu'on appelle des clés abrégées pour améliorer les performances et le stockage. Pour les types de données de chaîne de texte, avec la bibliothèque C standard, nous calculerions ces clés abrégées à l'aide de strxfrm() une fonction. Cependant, nous avons appris que de nombreuses bibliothèques C ont une variété de bogues et de mauvais comportements qui rendent cette approche peu fiable. Ainsi, l'optimisation des clés abrégées est actuellement désactivée pour les types de données de chaîne. Avec ICU, nous pouvons utiliser les appels d'API équivalents et calculer des clés abrégées de manière fiable et stable. Il y a donc également des améliorations de performances possibles à partir de ce déplacement.

Plus de classements

Outre ces améliorations internes de la robustesse et des performances, il existe également de nouvelles fonctionnalités destinées aux utilisateurs.

Pour certaines langues, plusieurs ordres de tri peuvent être pertinents en pratique. (Cela pourrait vous aider à démarrer.) Par exemple, pour l'allemand, il existe un ordre de tri standard utilisé dans la plupart des cas et un ordre de tri « annuaire téléphonique » utilisé pour les listes de noms. La bibliothèque C standard ne fournit qu'une seule de ces variantes (probablement la première). Mais si vous voulez écrire une application qui trie correctement, par exemple, les noms de produits et les noms de clients, vous devez pouvoir utiliser les deux.

Par exemple, l'exemple de Wikipedia allemand peut maintenant être reproduit avec 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

(Avec la glibc, COLLATE "de_DE" et COLLATE "de_AT" en effet renvoyer la première commande.)

Une façon intéressante de combiner plusieurs fonctionnalités pourrait être d'utiliser des domaines pour modéliser la différence mentionnée ci-dessus entre les noms de produits et les noms de clients :

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

(Ce n'est qu'un exemple. Bien sûr, vous pouvez également joindre ces COLLATE clauses directement aux définitions de colonne ou utilisez-les dans des requêtes.)

Encore plus de classements

Enfin, et c'est clairement ce que le monde attendait, il existe désormais un moyen de bien trier les emojis. Ceci est essentiel pour s'assurer que tous les visages de votre chat sont dans le bon ordre. Comparer

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

avec

=# 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
-----
 🙂
 🙃
 😶
 🙄
 😴
 😷
 😵
 🙁
 😺
 😸
 😹
 😻
 😼
 😽
 🙀
 😿
 😾

Oui, il existe en fait une norme à ce sujet.

Plus à venir

Ce n'est que le début. ICU offre de nombreuses fonctionnalités dans ce domaine que nous n'exposons pas encore via PostgreSQL. Il existe des options pour le tri insensible à la casse, le tri insensible aux accents et la personnalisation totale d'un classement. Recherchez-les dans les futures versions de PostgreSQL.