Todos os produtos
Search
Central de documentação

Lindorm:Design primary keys for Lindorm wide tables

Última atualização: Jul 02, 2026

O LindormTable é um mecanismo de dados distribuídos que particiona informações com base nas chaves primárias. Se a chave primária de uma tabela contiver várias colunas, o LindormTable as utilizará da esquerda para a direita durante as consultas. Um projeto inadequado de chave primária concentra leituras e gravações em poucas partições, criando hot spots que degradam o desempenho. Este tópico aborda os princípios para projetar chaves primárias eficientes e fornece exemplos para cargas de trabalho comuns.

Conceitos principais

Consultas GET vs. SCAN

O projeto da chave primária determina os métodos de consulta disponíveis.

Método de consulta

Funcionamento

Requisito

GET

Busca pontual por chave primária

Especifique todas as colunas da chave primária com valores explícitos

SCAN

Varredura de intervalo na chave primária

Especifique o intervalo na primeira coluna da chave primária; o sistema rejeita varreduras completas de tabela por padrão

Exemplos de consultas:

-- GET: retrieve a single order
SELECT * FROM table WHERE userid='abc' AND orderid=123

-- SCAN: retrieve a range of orders
SELECT * FROM table WHERE userid='abc' AND 123<orderid<456

-- SCAN with reverse order: retrieve most recent orders first
SELECT * FROM table WHERE userid='abc' AND 123<orderid<456 ORDER BY orderid DESC

Para consultas impossíveis de expressar com GET ou SCAN, utilize uma tabela de índice ou um índice secundário. Para obter mais informações, consulte SELECT.

O uso do método Scan retorna dados que não atendem aos critérios da consulta?

Não. O SQL de tabelas largas do Lindorm suporta diversos tipos de dados, cada um com codificação individual. Para ver os tipos de dados suportados, consulte Tipos de dados básicos.

Princípios de projeto

Unicidade

Diferentes versões de linha compartilham a mesma chave primária. Por padrão, uma consulta GET retorna apenas a versão mais recente. Projete chaves primárias exclusivas, a menos que pretenda usar intencionalmente o recurso de multiversão.

Uma chave primária pode ser uma única coluna ou uma composição de várias colunas:

  • [userid] — um registro por usuário

  • [userid][orderid] — vários registros por usuário, um por pedido

Quantidade de colunas e tamanho dos valores

Mantenha as chaves primárias compactas para reduzir custos de armazenamento e melhorar o desempenho de gravação.

  • Quantidade de colunas: Utilize de 1 a 3 colunas na chave primária.

  • Tamanho dos valores: Prefira valores de tamanho fixo, como inteiros longos. Para valores de tamanho variável, mantenha cada coluna abaixo de 2 KB.

Distribuição da primeira coluna

O Lindorm distribui os dados armazenados com base nas chaves primárias. Se a chave primária de uma tabela contiver várias colunas, a distribuição seguirá a ordem das colunas da esquerda para a direita. Quando os valores na primeira coluna da chave primária são enviesados ou aumentam monotonicamente, as gravações se acumulam em uma única partição.

A tabela a seguir mostra se os tipos comuns de primeira coluna produzem distribuição uniforme ou enviesada:

Primeira coluna da chave primária

Distribuição

Adequado?

ID de usuário (alta cardinalidade, aleatório)

Uniforme

Sim

ID de dispositivo (muitos dispositivos, acesso equilibrado)

Uniforme

Sim

Timestamp ou valor autoincremental

Sequencial — todas as gravações vão para uma partição

Não

Tipo de pedido ou código de status (poucos valores possíveis)

Enviesada — a maioria das gravações vai para poucas partições

Não

Coluna com prefixo compartilhado

Enviesada — valores similares caem na mesma partição

Não

Caso seja necessário usar uma coluna de timestamp ou autoincremental como primeira coluna da chave primária, aplique um prefixo hash para distribuir as gravações entre as partições. Consulte Evitar hot spots.

Evitar hot spots

Quando a primeira coluna da chave primária aumenta monotonicamente ou possui baixa cardinalidade, utilize uma das técnicas a seguir para distribuir as gravações uniformemente. Cada técnica apresenta diferentes compensações para as leituras.

Prefixo hash

Adicione um hash da chave original no início para dispersar as gravações entre as partições. Crie uma coluna derivada pk1 usando:

pk1 = hash(pk).substring(0, 4) + pk

Os primeiros quatro caracteres do hash funcionam como um prefixo aleatório. As gravações se espalham uniformemente e as leituras usam pk1 diretamente.

Chave primária: [pk1][...]

Compensação: Para consultar pela pk original, calcule o prefixo hash no momento da consulta para construir pk1.

Prefixo MD5

Uma variante comum do prefixo hash usando MD5:

Chave primária: [md5(userid).subStr(0,4)][userId][orderid]

Índice reverso

Inverta a representação em string da chave para quebrar prefixos sequenciais:

Chave primária: [reverse(userid)][orderid]

Compensação: Varreduras de intervalo em userid tornam-se menos naturais. Funciona melhor quando buscas por correspondência exata predominam.

Bucket modular

Divida as gravações em um número fixo de buckets usando aritmética modular. Atribua cada linha a um bucket com base em um valor monotonicamente crescente:

long bucket = timestamp % numBuckets

Chave primária: [bucket][timestamp][hostname][log-event]

Compensação: Para consultar todos os dados de um intervalo de tempo, emita uma consulta por bucket e mescle os resultados.

Salt aleatório

Anexe um número aleatório para espalhar as gravações entre as partições:

Chave primária: [userId][orderid][random(100)]

Compensação: Leituras para um único [userId][orderid] exigem varredura em vários valores de salt. Use este padrão apenas quando a distribuição de gravação for mais crítica do que a simplicidade de leitura.

Simplificar chaves primárias

Reduzir o tamanho da chave acelera as gravações e diminui os custos de armazenamento. Duas abordagens comuns:

  • Substitua STRING por LONG ou INT. Exemplo: '2015122410'Long(2015122410).

  • Substitua nomes longos por códigos curtos. Exemplo: 'taobao''tb'.

Considerações sobre problemas

  • P: A chave primária é única?

    R: No Lindorm, chaves primárias idênticas são consideradas múltiplas versões do mesmo dado. Como as consultas retornam por padrão a versão mais recente, geralmente é necessário garantir que a chave primária seja única, exceto ao utilizar o recurso de multiversão.

    Exemplo de melhor prática de projeto: A chave primária pode ser uma única coluna ou uma combinação de várias colunas. Cada chave primária representa um registro.

    • [userid]: Indica que a chave primária tem apenas uma coluna, com um único registro por usuário.

    • [userid][orderid]: Indica que a chave primária é uma combinação de duas colunas, permitindo múltiplos registros por usuário.

  • P: Quais cenários de consulta são atendidos?

    R: O projeto da chave primária limita a forma de consulta dos dados. O servidor do Lindorm compila uma instrução SELECT em dois métodos de consulta.

    • Consulta por chave primária completa (método get), por exemplo SELECT * FROM table WHERE userid='abc' AND orderid=123.

      Nota

      O método get exige o conhecimento de todas as colunas da chave primária. Ou seja, os valores de todos os campos que compõem a chave primária devem ser determinados.

    • Consulta por intervalo de chave primária (método scan), por exemplo SELECT * FROM table WHERE userid='abc' AND 123<orderid<456.

      Nota

      O método scan requer a especificação do intervalo da primeira coluna da chave primária. Caso contrário, o Lindorm rejeitará por padrão consultas ineficientes de varredura completa da tabela. Para detalhes, consulte Instruções de consulta ineficientes e explicações.

    Exemplo de melhor prática de projeto: Como implementar consultas complexas com métodos de consulta limitados? Os métodos a seguir podem ajudar.

    • Crie uma nova tabela para servir como tabela de índice.

    • Se as condições da consulta especificarem um intervalo em colunas que não são chave primária, o servidor usará filtros para descartar dados desnecessários.

    • Utilize índices secundários.

    • Use o método ORDER BY para implementar ordem decrescente (colocando dados novos primeiro), por exemplo SELECT * FROM table WHERE userid='abc' AND 123<orderid<456 ORDER BY orderid DESC.

      Nota

      Como o desempenho da ordenação inversa à ordem original dos campos da tabela é inferior ao da ordenação direta, considere refletir isso no projeto da chave primária se a maioria dos cenários exigir ordem decrescente. Defina a chave primária como [userid][orderid DESC].

  • P: Quais fatores devem ser considerados ao projetar a chave primária?

    R: Considere o tamanho dos valores das colunas da chave primária e a quantidade de colunas.

    • Tamanho dos valores das colunas da chave primária: Mantenha os valores das colunas da chave primária o mais curtos possível. Prefira tipos de tamanho fixo, como inteiros longos. Para tipos de tamanho variável, limite o tamanho do valor da coluna da chave primária a 2 KB para reduzir custos de armazenamento e melhorar o desempenho de gravação.

    • Quantidade de colunas da chave primária: Menos colunas resultam em maior desempenho de gravação e menores custos de armazenamento. Limite a quantidade de colunas da chave primária a 1-3.

  • Quais situações evitar ao projetar a chave primária?

    Lindorm é um banco de dados distribuído onde os dados são particionados conforme a chave primária. Se houver múltiplas colunas na chave primária, a distribuição segue o princípio de correspondência mais à esquerda do banco de dados. Para evitar problemas de hot spots de gravação, siga as condições abaixo:

    • A primeira coluna da chave primária deve ser o mais dispersa possível; não use nomes de chave primária com o mesmo prefixo.

    • Evite usar dados com prefixos comuns ou autoincrementais como primeira coluna da chave primária ou coluna de índice (por exemplo, colunas de timestamp).

    • Evite usar campos com prefixos óbvios ou enumerações (como order_type) como primeira chave primária.

      Se situações semelhantes forem inevitáveis, adicione um Hash para dispersar os dados. Exemplo:

      Suponha que a chave primária original pk seja uma string crescente. Defina uma nova chave primária pk1 = hash(pk1).substring(0,4)+pk, ou seja, primeiro disperse com um algoritmo Hash e selecione os primeiros 4 dígitos como prefixo concatenado à chave primária original pk.

  • P: Se os dados estiverem suficientemente dispersos, ainda haverá fenômenos de hot spots com acúmulo?

    R: O objetivo do hash é dispersar os dados em diferentes partições, evitando hot spots que deixariam um servidor sobrecarregado enquanto outros permanecem ociosos. Isso aproveita as vantagens da distribuição e concorrência.

    Exemplos de melhores práticas de projeto:

    • Projete um algoritmo de hash md5, definindo a chave primária como [md5(userid).subStr(0,4)][userId][orderid] .

    • Projete uma inversão, definindo a chave primária como [reverse(userid)][orderid].

    • Projete uma operação modular, definindo a chave primária como [bucket][timestamp][hostname][log-event]; long bucket = timestamp % numBuckets.

    • Adicione números aleatórios, definindo a chave primária como [userId][orderid][random(100)].

  • P: A chave primária pode ser mais simplificada?

    R: Colunas de chave primária simplificadas reduzem o volume de dados e aumentam a eficiência de consultas e gravações.

    Exemplos de melhores práticas de projeto:

    • Use Long ou Int em vez de String, por exemplo '2015122410' => Long(2015122410) .

    • Use codificações em vez de nomes, por exemplo '淘宝'=> 'tb'.

  • P: O uso do método scan retorna dados indesejados?

    R: Não. O SQL de tabelas largas do Lindorm suporta diversos tipos de dados, cada um com codificação individual. Para ver os tipos de dados suportados, consulte Tipos de dados básicos.

Projetos comuns

Dados de log e séries temporais

Objetivo da consulta

Projeto da chave primária

Consultar todos os registros de uma métrica em um intervalo de tempo

[hostname][log-event][timestamp]

Recuperar os registros mais recentes de uma métrica

[hostname][log-event][timestamp DESC]

Consultar dados temporais com alto volume de gravação (timestamp quente)

long bucket = timestamp % numBuckets;[bucket][timestamp][hostname][log-event]

Para consultas de "registros mais recentes", defina timestamp DESC na chave primária para classificar novos registros no topo. Um SCAN então retorna os registros mais recentes sem necessidade de ordenação adicional.

Para séries temporais de alto volume onde uma única coluna de timestamp criaria uma partição quente, o padrão de bucket modular distribui as gravações. Para ler todos os registros em um intervalo de tempo, consulte cada bucket separadamente e mescle os resultados.

Dados transacionais

Cargas de trabalho transacionais normalmente exigem múltiplos padrões de acesso — por vendedor, por comprador e por ID de pedido. Projete tabelas separadas para cada padrão de acesso:

Padrão de acesso

Tabela

Projeto da chave primária

Registros de transação do vendedor por tempo

Tabela do vendedor

[seller_id][timestamp][order_number]

Registros de transação do comprador por tempo

Tabela do comprador

[buyer_id][timestamp][order_number]

Busca de pedido por ID

Tabela de pedidos

[order_number]

Una as três tabelas para realizar consultas quando precisar acessar dados em todos os três padrões de acesso.