Pular para o conteúdo principal
O tipo de dado Map(K, V) armazena pares chave-valor. Ao contrário de outros bancos de dados, os maps no ClickHouse não exigem chaves únicas, ou seja, um map pode conter dois elementos com a mesma chave. (O motivo é que os maps são implementados internamente como Array(Tuple(K, V)).) Você pode usar a sintaxe m[k] para obter o valor da chave k no map m. Além disso, m[k] faz uma varredura no map, ou seja, o runtime da operação é linear em relação ao tamanho do map. Parâmetros
  • K — O tipo das chaves do Map. Qualquer tipo, exceto Nullable e LowCardinality aninhado com tipos Nullable.
  • V — O tipo dos valores do Map. Qualquer tipo.
Exemplos Crie uma tabela com uma coluna do tipo map:
Query
Para selecionar os valores de key2:
Query
Response
Se a chave solicitada k não estiver presente no map, m[k] retornará o default value do tipo de valor, por exemplo, 0 para tipos inteiros e '' para tipos String. Para verificar se uma chave existe em um map, você pode usar a função mapContains.
Query
Response

Convertendo Tuple para Map

Valores do tipo Tuple() podem ser convertidos para o tipo Map() usando a função CAST: Exemplo
Query
Response

Leitura das subcolunas de Map

Para evitar ler o Map inteiro, em alguns casos você pode usar as subcolunas keys e values. Exemplo
Query
Response

Serialização em buckets de Map no MergeTree

Por padrão, uma coluna Map no MergeTree é armazenada como um único fluxo Array(Tuple(K, V)). Ler uma única chave com m['key'] exige varrer a coluna inteira — cada par chave-valor de cada linha — mesmo quando apenas uma chave é necessária. Em maps com muitas chaves distintas, isso se torna um gargalo. A serialização em buckets (with_buckets) divide os pares chave-valor em vários subfluxos independentes (buckets) com base no hash da chave. Quando uma consulta acessa m['key'], apenas o bucket que contém essa chave é lido do disco, ignorando todos os outros buckets.

Habilitando a serialização em buckets

Para evitar deixar as inserções mais lentas, você pode manter a serialização basic para as partes de nível zero (criadas durante INSERT) e usar with_buckets apenas para as partes mescladas:

Como funciona

Quando uma parte de dados é gravada com a serialização with_buckets:
  1. O número médio de chaves por linha é calculado com base nas estatísticas do bloco.
  2. O número de buckets é determinado pela estratégia configurada (consulte Configurações).
  3. Cada par chave-valor é atribuído a um bucket por meio do hash da chave: bucket = hash(key) % num_buckets.
  4. Cada bucket é armazenado como um subfluxo independente com suas próprias chaves, valores e offsets.
  5. Um fluxo de metadados buckets_info registra a contagem de buckets e as estatísticas.
Quando uma consulta lê uma chave específica (m['key']), o otimizador reescreve a expressão para uma subcoluna de chave (m.key_<serialized_key>). A camada de serialização calcula a qual bucket a chave solicitada pertence e lê apenas esse bucket do disco. Quando o map completo é lido (por exemplo, SELECT m), todos os buckets são lidos e remontados no map original. Isso é mais lento do que a serialização basic devido à sobrecarga de ler e mesclar vários subfluxos.
A ordem das chaves dentro de um valor de map pode diferir da ordem de inserção original ao usar a serialização with_buckets. As chaves são distribuídas entre buckets por hash e remontadas na ordem dos buckets, não na ordem de inserção. Com a serialização basic, a ordem das chaves dos maps inseridos é preservada.
A contagem de buckets pode variar entre partes. Quando partes com diferentes contagens de buckets são mescladas, a contagem de buckets da nova parte é recalculada com base nas estatísticas mescladas. Partes com serialização basic e with_buckets podem coexistir na mesma tabela e são mescladas de forma transparente.

Configurações

Trade-offs de desempenho

A tabela a seguir resume o impacto no desempenho de with_buckets em comparação com a serialização basic em diferentes tamanhos de map (de 10 a 10.000 chaves por linha). A quantidade de buckets foi determinada pela estratégia sqrt, limitada a 32. Os números exatos dependem dos tipos de chave/valor, da distribuição dos dados e do hardware.

Recomendações

  • Maps pequenas (< 32 chaves em média): Mantenha a serialização basic. A sobrecarga da divisão em buckets não se justifica para maps pequenas. O valor padrão map_buckets_min_avg_size = 32 garante isso automaticamente.
  • Maps médias (32–100 chaves): Use with_buckets com a estratégia sqrt se as consultas acessarem chaves individuais com frequência. O ganho de velocidade é de 4–8x para buscas de uma única chave.
  • Maps grandes (100+ chaves): Use with_buckets. Buscas de uma única chave são 16–49x mais rápidos. Considere map_serialization_version_for_zero_level_parts = 'basic' para manter a velocidade de inserção próxima à referência.
  • Varreduras completas do map dominam o workload: Mantenha basic. A serialização em buckets adiciona ~2x de sobrecarga para varreduras completas.
  • Workload misto (alguns buscas de chave, algumas varreduras completas): Use with_buckets com as partes de nível zero definidas como basic. A otimização PREWHERE lê apenas o bucket relevante para o filtro e depois lê o map completo somente para as linhas correspondentes, proporcionando um ganho líquido de velocidade significativo.

Abordagens alternativas

Se a serialização Map em buckets não se adequar ao seu caso de uso, há duas abordagens alternativas para melhorar o desempenho do acesso por chave:

Usando o tipo de dados JSON

O tipo de dados JSON armazena cada caminho frequente como uma subcoluna dinâmica separada. Os caminhos que excedem o limite de max_dynamic_paths vão para uma estrutura de dados compartilhada, que pode usar a serialização advanced para otimizar leituras de um único caminho. Consulte o artigo no blog para ter uma visão geral detalhada da serialização advanced. Use JSON quando chaves diferentes precisarem de tipos de valor diferentes, quando o conjunto de chaves variar significativamente entre as linhas ou quando chaves acessadas com frequência forem conhecidas de antemão e puderem ser declaradas como caminhos tipados para acesso direto por subcoluna.

Sharding manual em várias colunas do tipo Map

Você pode dividir manualmente um único Map em várias colunas com base no hash da chave, no nível da aplicação:
Durante a inserção, direcione cada par chave-valor para a coluna m{hash(key) % 4}. Nas consultas, leia da coluna específica: m{hash('target_key') % 4}['target_key']. O sharding manual é vantajoso quando as mesclagens verticais são importantes para reduzir o uso de memória durante mesclagens de tabelas com muitas colunas, ou quando o número de shards precisa ser fixo e controlado explicitamente. Para a maioria dos casos de uso, a serialização automática em buckets é mais simples e suficiente. Veja também
Última modificação em 10 de junho de 2026