Estamos projetando um sistema que é conhecido por ser de leitura pesada (da ordem de dezenas de milhares de leituras por minuto).
- Há uma tabela
names
que serve como uma espécie de registro central. Cada linha tem umtext
camporepresentation
e um exclusivokey
que é um hash MD5 daquelerepresentation
. 1 Esta tabela tem atualmente dezenas de milhões de registros e espera-se que cresça em bilhões ao longo da vida útil do aplicativo. - Existem dezenas de outras tabelas (de esquemas e contagens de registros altamente variados) que fazem referência à
names
tabela. Qualquer registro em uma dessas tabelas tem a garantia de ter umname_key
, que é funcionalmente uma chave estrangeira para anames
tabela.
1: Aliás, como você pode esperar, os registros nesta tabela são imutáveis uma vez gravados.
Para qualquer tabela que não seja a names
tabela, a consulta mais comum seguirá este padrão:
SELECT list, of, fields
FROM table
WHERE name_key IN (md5a, md5b, md5c...);
Eu gostaria de otimizar o desempenho de leitura. Suspeito que minha primeira parada deve ser minimizar o tamanho dos índices (embora eu não me importe de ser provado errado lá).
A pergunta:
Qual é/são os tipos de dados ideais para as colunas key
e ?
Existe uma razão para usar mais ? ou ?name_key
hex(32)
bit(128)
BTREE
GIN
O tipo de dados
uuid
é perfeitamente adequado para a tarefa. Ele ocupa apenas 16 bytes em oposição a 37 bytes na RAM para a representaçãovarchar
outext
. (Ou 33 bytes no disco, mas o número ímpar exigiria preenchimento em muitos casos para torná-lo 40 bytes efetivamente.) E ouuid
tipo tem mais algumas vantagens.Exemplo:
Ver:
Você pode considerar outras funções de hashing (um pouco mais baratas) se não precisar do componente criptográfico do md5, mas eu usaria o md5 para o seu caso de uso. md5 está bem estabelecido, muito rápido e seus valores são principalmente somente leitura de qualquer maneira.
Uma palavra de advertência : Para o seu caso (
immutable once written
) uma PK funcionalmente dependente (pseudo-natural) está bem. Mas o mesmo seria uma dor onde as atualizaçõestext
são possíveis. Pense em corrigir um erro de digitação: o PK e todos os índices dependentes, colunas FK em "dezenas de outras tabelas" e outras referências também teriam que mudar. Tabelas e índices inchados, problemas de bloqueio, atualizações lentas, referências perdidas, ...Se
text
puder mudar em operação normal, um PK substituto seria uma escolha melhor. Sugiro umabigserial
coluna com um intervalo de -9223372036854775808 a +9223372036854775807. Isso é nove quintilhões duzentos e vinte e três quatrilhões trezentos e setenta e dois trilhões trinta e seis algo bilhões ) valores distintos para "bilhões de linhas". Pode ser uma boa ideia em qualquer caso: 8 em vez de 16 bytes para dezenas de colunas e índices FK!). Ou um UUID aleatório para cardinalidades muito maiores ou sistemas distribuídos. Você sempre pode armazenar o referido md5 (asuuid
) adicionalmente para encontrar linhas na tabela principal do texto original rapidamente.Relacionado:
Para sua consulta , consulte:
E os hífens?
Se preferir uma representação sem hífens, remova os hífens para exibição:
Mas eu não me incomodaria. A representação padrão é muito boa. E o problema realmente não é a representação aqui.
Se outras partes devem ter uma abordagem diferente e lançar strings sem hífens na mistura, isso também não é problema. O Postgres aceita várias representações de texto razoáveis como entrada para um arquivo
uuid
. O manual :Por que não
bytea
?A
md5()
função retornatext
. Você usariadecode()
para converterbytea
e a representação padrão disso é:Você teria que
encode()
novamente para obter a representação de texto original:Para completar, os valores armazenados como
bytea
ocupariam 20 bytes na RAM (e 17 bytes no disco, 24 com preenchimento ) devido à sobrecarga internavarlena
, que é particularmente desfavorável para tamanho e desempenho de índices simples.E os UUIDs "inválidos"?
Não há UUIDs "inválidos".
Octeto 13 e 17 codificam uma "versão" e uma "variante" para certos tipos de UUID. Mas o tipo de dados do Postgres
uuid
aceita todas as quantidades de 128 bits sem considerar "versão" ou "variante". Isso está de acordo com a RFC 4122 :"Versão" e "variante" não fazem sentido/não se aplicam a este caso de uso. Para verificar, fiz um teste rápido:
db<>fique aqui
Tudo funciona a favor de um
uuid
aqui.Eu armazenaria o MD5 em uma coluna
text
ou .varchar
Não há diferença de desempenho entre os vários tipos de dados de caracteres. Você pode querer restringir o comprimento dos valores md5 usandovarchar(xxx)
para garantir que o valor md5 nunca exceda um determinado comprimento.Listas IN grandes geralmente não são muito rápidas, é melhor fazer algo assim:
Outra opção que às vezes é considerada mais rápida é usar um array:
Como você está apenas comparando por igualdade, um índice BTree regular deve ser bom. Ambas as consultas devem poder usar esse índice (especialmente se estiverem selecionando apenas uma pequena fração das linhas.
Outra opção é usar 4 colunas INTEGER ou 2 colunas BIGINT.