PostgreSQL
 sql >> Base de Dados >  >> RDS >> PostgreSQL

Agrupamentos mais robustos com suporte ICU no PostgreSQL 10


Neste artigo, quero apresentar o suporte ICU no PostgreSQL, no qual trabalhei para o PostgreSQL versão 10, que será lançado ainda este ano.

Classificação


A classificação é uma funcionalidade importante de um sistema de banco de dados. Primeiro, os usuários geralmente querem ver os dados classificados. Qualquer resultado de consulta que contenha mais de uma linha e seja destinado ao consumo do usuário final provavelmente desejará ser classificado, apenas para uma melhor experiência do usuário. Em segundo lugar, grande parte da funcionalidade interna de um sistema de banco de dados depende da classificação de dados ou da disponibilidade de dados classificados. Os índices de árvore B são um exemplo óbvio. Os índices BRIN têm conhecimento de ordem. O particionamento de intervalo precisa comparar valores. As junções de mesclagem dependem da entrada classificada. A ideia comum a essas diferentes técnicas é que, grosso modo, se você classificou os dados e sabe o que está procurando, fica muito mais rápido localizar o local onde eles devem ser encontrados.

Há dois aspectos importantes na classificação. Um deles é o algoritmo de ordenação. Este é um tópico padrão em ciência da computação, e muito trabalho foi feito no PostgreSQL ao longo dos anos para refinar os vários algoritmos e métodos de classificação, mas não é sobre isso que vou escrever. A outra é decidir em que ordem as coisas devem ser, que é o que chamamos de agrupamento. Em muitos casos, essa escolha é óbvia. 1 vem antes de 2. FALSE vem antes de TRUE... bem, alguém decidiu arbitrariamente isso. A geralmente vem antes de B. Mas quando se trata de texto em linguagem natural, as coisas ficam interessantes. Há muitas maneiras diferentes de ordenar texto, e os métodos reais para agrupar strings de texto são mais complicados do que pode parecer. Diferentes idiomas preferem diferentes ordens de classificação, mas mesmo dentro de um idioma, pode haver variações para diferentes aplicativos. E há detalhes com os quais se preocupar, como o que fazer com espaços em branco, pontuação, diferenças entre maiúsculas e minúsculas, sinais diacríticos e assim por diante. Procure o algoritmo de agrupamento Unicode para obter mais informações sobre isso.

Antes que o recurso ICU fosse confirmado, toda essa funcionalidade era facilitada pela biblioteca C no sistema operacional. PostgreSQL basicamente apenas passa strings para strcmp() , strcoll() , e afins e trabalha com o resultado. As bibliotecas C nos vários sistemas operacionais implementam as várias variantes e nuances de agrupamento mencionadas acima para diferentes níveis de funcionalidade e qualidade, de modo que o PostgreSQL pode fazer o que seu sistema operacional pode fazer.

Alterando agrupamentos


Os problemas começam se o sistema operacional precisar alterar um agrupamento fornecido. Por que eles iriam querer fazer isso? Pode ser que o agrupamento anterior esteja errado e precise ser corrigido. Talvez um novo padrão para um idioma tenha sido publicado e o agrupamento deva ser atualizado para isso. Talvez a representação interna de dados de agrupamento e string tenha sido alterada por motivos de desempenho ou porque era necessário implementar funcionalidades adicionais. Para muitos programas, isso não é um problema. Você pode ver uma saída ordenada ligeiramente diferente, se notar alguma diferença. Para um sistema de banco de dados, no entanto, este é um grande problema. Conforme descrito acima, o PostgreSQL armazena dados classificados em índices e outros locais e depende da ordem de classificação para estar correta. Se a ordem de classificação não estiver correta, uma pesquisa de índice pode não localizar dados que estejam realmente lá. Ou uma gravação em um índice gravará em um local diferente. Ou os dados são gravados ou lidos na partição errada. Isso pode levar a dados duplicados erroneamente ou à aparência de perda de dados porque os dados não estão onde são procurados. Em outras palavras, pode levar à corrupção de dados e (aparente) perda de dados.

Infelizmente, não havia muito que pudéssemos fazer sobre isso até agora. Os sistemas operacionais atualizam seus agrupamentos sempre que quiserem, talvez como parte de uma atualização de seu pacote de biblioteca C. Não há como descobrir isso de maneira razoável, ou talvez inspecionando os pacotes de atualização em detalhes. E mesmo assim, você rejeitará uma atualização importante de sua biblioteca C porque notou que o agrupamento em algum local que você não está usando foi alterado? Foi uma situação muito desconfortável.

Entrar na UTI


Então, onde entra a UTI? ICU, International Components for Unicode, é uma biblioteca que fornece recursos de internacionalização e localização, incluindo agrupamento. Portanto, a esse respeito, é uma alternativa ao uso dos recursos da biblioteca C padrão. O bom é que o ICU fornece explicitamente algumas garantias sobre a estabilidade dos agrupamentos:
  • Um agrupamento não será alterado de maneira incompatível como parte de uma atualização de versão secundária.
  • Um agrupamento tem uma versão, que pode ser inspecionada, e quando um agrupamento é alterado de forma incompatível, a versão muda.

Para usuários do PostgreSQL, isso significará na prática:
  • As atualizações de rotina do pacote do sistema operacional não interferem na validade dos dados classificados. Como um postgres binário está vinculado a uma versão principal específica do libicu , atualizações rotineiras de pacotes do sistema operacional não terminarão com postgres sendo vinculado a uma nova versão principal do libicu , contanto que a) você não atualize os pacotes PostgreSQL, ou b) os pacotes PostgreSQL ainda estejam vinculados à mesma versão principal do ICU como antes. Os empacotadores precisarão ter cuidado para manter isso corretamente, mas isso não deve ser muito problemático na prática.
  • Quando as principais atualizações de pacotes e sistemas operacionais alteram a versão de um agrupamento, temos uma maneira de detectar isso e avisar o usuário. No momento, apenas alertamos e oferecemos algumas diretrizes e ferramentas para corrigir as coisas, mas no futuro poderemos refinar e automatizar ainda mais isso.

(Para tornar isso mais explícito para os empacotadores:em uma ramificação estável do seu sistema operacional, você não deve alterar a versão principal do ICU à qual um determinado conjunto de pacotes do PostgreSQL está vinculado.)

Usando UTI


Para poder usar isso, o PostgreSQL precisa ser construído explicitamente com suporte ICU. Ao compilar a partir da fonte, use ./configure --with-icu juntamente com outras opções desejadas. Esperamos que a maioria dos principais pacotes binários ofereça isso por padrão também. Quando isso é feito, os agrupamentos baseados em ICU são oferecidos juntamente com os agrupamentos baseados em libc que as versões anteriores ofereciam. (Assim, construir com suporte ICU não remove o suporte de agrupamento libc; os dois existem juntos.) Verifique a documentação para obter detalhes sobre como selecionar um agrupamento baseado em ICU versus um baseado em libc. Por exemplo, se você tiver especificado anteriormente
CREATE TABLE ... (... x text COLLATE "en_US" ...)

você pode agora fazer
CREATE TABLE ... (... x text COLLATE "en-x-icu" ...)

Isso deve fornecer aproximadamente o mesmo comportamento visível ao usuário de antes, exceto que seu banco de dados estará mais à prova de futuro quando se trata de atualização. (No Linux/glibc, a ordem de classificação deve ser basicamente a mesma, mas pode haver pequenas diferenças em alguns detalhes. Se, no entanto, você estiver usando um sistema operacional cuja biblioteca C não suporta agrupamento Unicode, como macOS ou versões mais antigas do FreeBSD, então esta será uma grande mudança - para melhor.)

Atualmente, o suporte ICU está disponível apenas para agrupamentos explicitamente especificados. O agrupamento padrão em um banco de dados ainda é sempre fornecido pela biblioteca C. Abordar isso é um projeto futuro.

Se você atualizar esse banco de dados por pg_upgrade por exemplo, para uma nova instalação do PostgreSQL que está vinculada a uma versão principal mais recente do ICU que alterou a versão de agrupamento desse agrupamento que você está usando, você receberá um aviso e terá que corrigir, por exemplo, quaisquer índices que dependam do agrupamento. As instruções para isso também estão na documentação.

Teclas abreviadas


Portanto, essa mudança fornecerá algumas melhorias muito importantes para a robustez de longo prazo de um sistema de banco de dados. Mas ICU também é uma melhoria em relação à biblioteca do sistema C em outras áreas.

Por exemplo, as árvores B do PostgreSQL podem armazenar as chamadas chaves abreviadas para melhorar o desempenho e o armazenamento. Para tipos de dados de string de texto, com a biblioteca C padrão, calcularíamos essas chaves abreviadas usando o strxfrm() função. No entanto, aprendemos que muitas bibliotecas C têm uma variedade de bugs e mau comportamento que tornam essa abordagem não confiável. Portanto, a otimização de chaves abreviadas está atualmente desabilitada para tipos de dados de string. Com o ICU, podemos usar as chamadas de API equivalentes e calcular as chaves abreviadas da maneira que acreditamos ser confiável e estável. Portanto, também há possíveis melhorias de desempenho com esse movimento.

Mais agrupamentos


Além dessas melhorias internas de robustez e desempenho, há também algumas novas funcionalidades voltadas para o usuário.

Para alguns idiomas, mais de uma ordem de classificação pode ser relevante na prática. (Isso pode ajudar você a começar.) Um exemplo é que, para o alemão, há uma ordem de classificação padrão que é usada para a maioria dos propósitos e uma ordem de classificação de “catálogo telefônico” que é usada para listas de nomes. A biblioteca C padrão fornece apenas uma dessas variantes (provavelmente a primeira). Mas se você quiser escrever um aplicativo que classifique corretamente, digamos, nomes de produtos e nomes de clientes, você precisa ser capaz de usar ambos.

Por exemplo, o exemplo da Wikipedia alemã agora pode ser reproduzido com 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

(Com glibc, COLLATE "de_DE" e COLLATE "de_AT" de fato, devolva o primeiro pedido.)

Uma maneira interessante de combinar vários recursos pode ser usar domínios para modelar a diferença mencionada acima entre nomes de produtos e nomes de clientes:
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";

(Este é apenas um exemplo. Claro que você também pode anexar esses COLLATE cláusulas para definições de coluna diretamente ou use-as em consultas.)

Ainda mais agrupamentos


Finalmente, e isso é claramente o que o mundo estava esperando, agora existe uma maneira de classificar corretamente os emojis. Isso é essencial para garantir que todos os rostos do seu gato estejam na ordem correta. Comparar
=# SELECT chr(x) FROM generate_series(x'1F634'::int, x'1F644'::int) AS _(x)
       ORDER BY chr(x) COLLATE "und-x-icu";
 chr
-----
 😴
 😵
 😶
 😷
 😸
 😹
 😺
 😻
 😼
 😽
 😾
 😿
 🙀
 🙁
 🙂
 🙃
 🙄

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

Sim, existe realmente um padrão sobre isso.

Mais por vir


Isto é apenas o começo. O ICU oferece muitas funcionalidades nesta área que ainda não estamos expondo através do PostgreSQL. Existem opções para classificação sem distinção entre maiúsculas e minúsculas, classificação sem distinção entre acentos e personalização total de um agrupamento. Procure por eles em versões futuras do PostgreSQL.