Ao escolher a chave primária geralmente você também escolhe a chave clusterizada. Os dois são muitas vezes confundidos, mas você tem que entender a diferença.
Chaves primárias são negócios lógicos elementos. A chave primária é usada pelo seu aplicativo para identificar uma entidade, e a discussão sobre chaves primárias é principalmente sobre o uso de chaves naturais ou chaves substitutas. Os links são muito mais detalhados, mas a ideia básica é que as chaves naturais são derivadas de uma propriedade de entidade existente como
ssn
ou phone number
, enquanto as chaves substitutas não têm nenhum significado em relação à entidade comercial, como id
ou rowid
e geralmente são do tipo IDENTITY
ou algum tipo de uuid. Minha opinião pessoal é que as chaves substitutas são superiores às chaves naturais, e a escolha deve ser sempre valores de identidade para aplicativos apenas locais, guias para qualquer tipo de dados distribuídos. Uma chave primária nunca muda durante o tempo de vida da entidade. Chaves agrupadas são a chave que define o armazenamento físico das linhas na tabela. Na maioria das vezes, eles se sobrepõem à chave primária (o identificador de entidade lógica), mas isso não é realmente aplicado nem exigido. Quando os dois são diferentes, significa que há um índice exclusivo não clusterizado na tabela que implementa a chave primária. Os valores de chave agrupados podem, na verdade, mudar durante o tempo de vida da linha, resultando na movimentação física da linha na tabela para um novo local. Se você precisar separar a chave primária da chave clusterizada (e às vezes você o faz), escolher uma boa chave clusterizada é significativamente mais difícil do que escolher uma chave primária. Há dois fatores principais que impulsionam seu design de chave clusterizada:
- O padrão de acesso a dados predominante .
- As considerações de armazenamento .
Padrão de acesso a dados . Com isso entendo a forma como a tabela é consultada e atualizada. Lembre-se de que as chaves agrupadas determinam a ordem real das linhas na tabela. Para determinados padrões de acesso, alguns layouts fazem toda a diferença do mundo no que diz respeito à velocidade de consulta ou à simultaneidade de atualização:
-
dados atuais versus dados de arquivo. Em muitas aplicações, os dados do mês atual são acessados com frequência, enquanto os do passado raramente são acessados. Nesses casos, o design da tabela usa particionamento de tabela por data de transação, muitas vezes usando um algoritmo de janela deslizante. A partição do mês atual é mantida no grupo de arquivos localizado em um disco rápido, os dados antigos arquivados são movidos para grupos de arquivos hospedados em armazenamento mais barato, mas mais lento. Obviamente, neste caso, a chave agrupada (data) não é a chave primária (id da transação). A separação dos dois é orientada pelos requisitos de escala, pois o otimizador de consultas será capaz de detectar que as consultas estão interessadas apenas na partição atual e nem mesmo olhar para as históricas.
-
Processamento de estilo de fila FIFO. Nesse caso, a tabela tem dois pontos de acesso:a cauda onde ocorrem as inserções (enfileirar) e a cabeça onde ocorrem as exclusões (desenfileirar). A chave agrupada deve levar isso em consideração e organizar a tabela para separar fisicamente a localização da cauda e da cabeça no disco, a fim de permitir a simultaneidade entre enfileirar e desenfileirar, por exemplo. usando uma chave de ordem de enfileiramento. Em puro enfileira esta chave agrupada é a única chave, pois não há chave primária na tabela (ela contém mensagens , não entidades ). Mas na maioria das vezes a fila não é pura, ela também atua como armazenamento para as entidades e a linha entre a fila e a tabela está borrado. Neste caso, há também uma chave primária, que não pode ser a chave clusterizada:as entidades podem ser reenfileiradas, alterando assim o valor da chave clusterizada da ordem de enfileiramento, mas não podem alterar o valor da chave primária. A falha em ver a separação é a principal razão pela qual as filas apoiadas por tabela de usuário são tão notoriamente difíceis de acertar e repletas de deadlocks:porque o enqueue e o dequeue ocorrem intercalados pela tabela, em vez de localizados no final e no início da fila.
-
Processamento correlacionado. Quando o aplicativo for bem projetado, ele particionará o processamento de itens correlacionados entre seus threads de trabalho. Por exemplo, um processador é projetado para ter 8 threads de trabalho (digamos, para corresponder às 8 CPUs no servidor) para que os processadores particionem os dados entre si, por exemplo. o trabalhador 1 pega apenas as contas nomeadas de A a E, o trabalhador 2 F a J etc. Nesses casos, a tabela deve ser agrupada pelo nome da conta (ou por uma chave composta que tem na posição mais à esquerda a primeira letra do nome da conta), para que os trabalhadores localizem suas consultas e atualizações na tabela. Tal mesa teria 8 pontos quentes distintos, ao redor da área que cada trabalhador concentra no momento, mas o importante é que eles não se sobreponham (sem bloqueio). Esse tipo de design é predominante em designs OLTP de alto rendimento e em cargas de benchmark TPCC, onde esse tipo de particionamento também reflete na localização da memória das páginas carregadas no buffer pool (localidade NUMA), mas discordo.
Considerações de armazenamento . A chave agrupada largura tem enormes repercussões na arrumação da mesa. Por um lado, a chave ocupa espaço em todas as páginas não-folha da b-tree, portanto, uma chave grande ocupará mais espaço. Em segundo lugar, e muitas vezes mais importante, é que a chave agrupada é usada como chave de pesquisa por todas as chaves não agrupadas, então todas chave não clusterizada terá que armazenar a largura total da chave clusterizada para cada linha. Isso é o que torna grandes chaves clusterizadas como varchar(256) e orienta escolhas ruins para chaves de índice clusterizado.
Além disso, a escolha da chave tem impacto na fragmentação do índice clusterizado, às vezes afetando drasticamente o desempenho.
Essas duas forças às vezes podem ser antagônicas, o padrão de acesso a dados exigindo uma certa chave de cluster grande que causará problemas de armazenamento. Nesses casos, é claro, é necessário um equilíbrio, mas não existe uma fórmula mágica. Você mede e testa para chegar ao ponto ideal.
Então, o que fazemos com tudo isso? Sempre comece considerando a chave clusterizada que também é a chave primária da forma
entity_id IDENTITY(1,1) NOT NULL
. Separe os dois e organize a tabela de acordo (por exemplo, partição por data) quando apropriado.