Escolher as propriedades de tabela do Hologres adequadas aos seus padrões de consulta reduz a quantidade de dados verificados, o número de arquivos acessados e as operações de I/O, resultando em menor latência e maior taxa de consultas por segundo (QPS). Este guia mapeia seis cenários comuns de consulta para as configurações de formato de armazenamento, chave primária, chave de distribuição, chave de clusterização, particionamento e índice bitmap mais indicadas para cada caso.
Escolha um formato de armazenamento
O Hologres oferece suporte a três formatos de armazenamento: orientado a linhas, orientado a colunas e híbrido (linha-coluna). Para detalhes sobre cada formato, consulte Configurar orientação de dados.
Utilize a árvore de decisão a seguir para selecionar o formato. Caso sua carga de trabalho ainda não esteja bem definida, comece com o armazenamento híbrido, pois ele equilibra os compromissos entre a maior variedade de padrões de consulta.

Referência rápida de propriedades
Cada propriedade de tabela atua em uma camada específica da execução de consultas. Compreender a função de cada uma ajuda você a escolher a combinação ideal para o seu cenário.
|
Propriedade |
Função |
Mais indicada para |
|
Chave primária |
Habilita o Fixed Plan para busca direta de linhas |
Consultas pontuais com QPS ultra-alto |
|
Chave de distribuição |
Agrupa linhas no mesmo shard pelo valor da chave; reduz o shuffle entre shards |
Consultas de agregação; consultas JOIN |
|
Chave de clusterização |
Ordena fisicamente as linhas pela chave dentro de cada arquivo; reduz I/O em varreduras por intervalo ou igualdade |
Varreduras por prefixo; consultas com filtro de valor único |
|
|
Ordena os dados dentro de cada arquivo por esta coluna; reduz o número de arquivos verificados em predicados de intervalo |
Consultas com filtros baseados em tempo |
|
Particionamento |
Elimina partições inteiras antes da varredura |
Tabelas grandes com padrões de acesso baseados em tempo |
|
Índice bitmap |
Mapeia cada valor distinto para as linhas correspondentes; restringe varreduras para filtros de valor único |
Consultas com filtros de valor único |
Configure propriedades de tabela por cenário de consulta
Após definir o formato de armazenamento, alinhe as demais propriedades da tabela aos seus padrões de consulta. Cada cenário abaixo descreve a configuração recomendada, explica o mecanismo envolvido e apresenta resultados de desempenho medidos.
Os exemplos nesta seção utilizam uma instância Hologres de 64 unidades de computação (CU) e o conjunto de dados TPC-H de 100 GB. O conjunto de dados contém duas tabelas: a tabela Orders (ondeo_orderkeyidentifica exclusivamente um pedido) e a tabela Lineitem (ondel_orderkeyel_linenumberidentificam conjuntamente um item de linha). Estes testes baseiam-se na metodologia de benchmarking TPC-H, mas não atendem a todos os requisitos oficiais; portanto, os resultados não podem ser comparados com benchmarks TPC-H publicados. Se sua tabela atende a múltiplos padrões de consulta com diferentes campos de filtro ou JOIN, configure as propriedades com base na consulta mais frequente ou mais sensível ao desempenho.
Cenário 1: Consultas pontuais com QPS ultra-alto
Padrão de consulta
Você precisa executar dezenas de milhares de consultas pontuais por segundo na tabela Orders. É possível especificar o campo o_orderkey para localizar uma linha de dados.
SELECT * FROM orders WHERE o_orderkey = ?;
Configuração
Defina o campo de filtro como chave primária. O Hologres gera um Fixed Plan para buscas por chave primária, ignorando o caminho geral de planejamento de consultas e reduzindo significativamente a sobrecarga de execução. Para mais detalhes, consulte Acelerar execução SQL com Fixed Plan.
Como funciona
O Fixed Plan ignora etapas de otimização de consulta desnecessárias para buscas de linha única, roteando as solicitações diretamente para a linha de destino. Sem uma chave primária, o mecanismo recorre a uma varredura completa do shard.
Resultados de desempenho (500 consultas simultâneas, tabela orientada a linhas ou híbrida)
|
Configuração |
QPS médio |
Latência média |
|
Com chave primária configurada |
~104.000 |
~4 ms |
|
Sem chave primária |
~16.000 |
~30 ms |
Para a instrução DDL, consulte DDL do Cenário 1.
Cenário 2: Varreduras por prefixo em conjunto de dados pequeno com alto QPS
Padrão de consulta
Este cenário aplica-se a consultas com as seguintes características: necessidade de processar dezenas de milhares de QPS, execução baseada em um campo da chave primária e retorno de um conjunto de dados contendo algumas ou dezenas de entradas.
SELECT * FROM lineitem WHERE l_orderkey = ?;
Essa consulta recupera todos os itens de linha de um único pedido, caracterizando uma varredura por prefixo em uma chave primária composta.
Configuração
Aplique as três configurações a seguir:
Ordenação da chave primária — Coloque o campo de filtro como primeiro elemento da chave primária composta. Utilize
(l_orderkey, l_linenumber), e não(l_linenumber, l_orderkey). Isso permite que o Hologres avalie o predicado de igualdade na chave principal sem verificar linhas não relacionadas.Chave de distribuição — Defina o campo de filtro como chave de distribuição (
l_orderkey). Assim, os dados de cada pedido ficam armazenados em um único shard, fazendo com que a consulta acesse exatamente um shard em vez de todos.Chave de clusterização (apenas para armazenamento orientado a colunas ou híbrido) — Configure o campo de filtro como chave de clusterização (
l_orderkey). Dentro de cada arquivo, as linhas são ordenadas por essa chave, o que reduz o I/O ao verificar um intervalo contíguo.
Com essas propriedades definidas, ative o Fixed Plan para varreduras por prefixo configurando o parâmetro Grand Unified Configuration (GUC) hg_experimental_enable_fixed_dispatcher_for_scan como on. Para detalhes, consulte Acelerar execução SQL com Fixed Plan.
Como funciona
A combinação correta de ordenação da chave primária, chave de distribuição colocalizada e chave de clusterização garante que todas as linhas correspondentes estejam no mesmo shard, fisicamente adjacentes no armazenamento e endereçadas diretamente pelo Fixed Plan.
Resultados de desempenho (tabela orientada a linhas ou híbrida)
|
Configuração |
Consultas simultâneas |
QPS médio |
Latência média |
|
Três propriedades configuradas |
500 |
~37.000 |
~13 ms |
|
Propriedades não configuradas |
1 |
~60 |
~16 ms |
Para a instrução DDL, consulte DDL do Cenário 2.
Cenário 3: Consultas com condições de filtro baseadas em tempo
Padrão de consulta
-- Original query
SELECT
l_returnflag,
l_linestatus,
sum(l_quantity) AS sum_qty,
sum(l_extendedprice) AS sum_base_price,
sum(l_extendedprice * (1 - l_discount)) AS sum_disc_price,
sum(l_extendedprice * (1 - l_discount) * (1 + l_tax)) AS sum_charge,
avg(l_quantity) AS avg_qty,
avg(l_extendedprice) AS avg_price,
avg(l_discount) AS avg_disc,
count(*) AS count_order
FROM
lineitem
WHERE
l_shipdate <= date '1998-12-01' - interval '120' day
GROUP BY
l_returnflag,
l_linestatus
ORDER BY
l_returnflag,
l_linestatus;
-- Modified query used in the performance test (narrowed time range to highlight the effect)
SELECT ... FROM lineitem
WHERE
l_year='1992' -- filters to a single partition
AND l_shipdate <= date '1992-12-01'
...;
Configuração
Particione por tempo — Adicione uma coluna
l_yeare utilize-a como chave de partição para dividir a tabela por ano. Decida se deve particionar a tabela ou apenas configurar a propriedadeevent_time_columncom base no volume de dados e nos requisitos de negócio. Verifique os limites e notas de uso em CREATE PARTITION TABLE antes de realizar o particionamento.event_time_column— Definal_shipdatecomoevent_time_column(chave de segmento). Isso assegura que as entradas de dados nos arquivos do shard sejam ordenadas com base na propriedadeevent_time_column, reduzindo o número de arquivos a serem verificados. Para detalhes, consulte Event Time Column (Segment Key).
Como funciona
O particionamento elimina partições inteiras antes que a consulta leia qualquer arquivo. Em seguida, a event_time_column elimina arquivos individuais dentro da partição restante, ordenando os dados em cada arquivo pela coluna configurada.
Resultados de desempenho (tabela orientada a colunas)
|
Configuração |
Arquivos verificados |
|
Partição + |
80 arquivos em 1 partição |
|
Sem partição; |
320 arquivos |
ExecuteEXPLAIN ANALYZEpara inspecionar as contagens reais de varredura. O parâmetroPartitions selectedmostra quantas partições foram acessadas; o parâmetrodopindica quantos arquivos foram verificados.
Para a instrução DDL, consulte DDL do Cenário 3.
Cenário 4: Consultas com condições de filtro de valor único não temporal
Padrão de consulta
SELECT
...
FROM
lineitem
WHERE
l_shipmode IN ('FOB', 'AIR');
Configuração
Chave de clusterização — Defina
l_shipmodecomo chave de clusterização. Linhas com o mesmo valor del_shipmodesão armazenadas consecutivamente em cada arquivo, permitindo que o mecanismo leia um bloco contíguo menor em vez de linhas espalhadas pelo arquivo.Índice bitmap — Configure
l_shipmodecomo uma coluna bitmap. O índice bitmap mapeia cada valor distinto para as linhas que o contêm, permitindo que o mecanismo salte diretamente para as linhas correspondentes sem ler dados não relacionados.
Como funciona
A chave de clusterização reduz o I/O agrupando fisicamente as linhas correspondentes. O índice bitmap restringe ainda mais a varredura filtrando no nível do bloco antes que quaisquer dados de linha sejam lidos.
Resultados de desempenho (tabela orientada a colunas)
|
Configuração |
Linhas lidas |
Duração da consulta |
|
Chave de clusterização + índice bitmap configurados |
170 milhões |
0,71 s |
|
Nenhum dos dois configurado |
600 milhões (tabela completa) |
2,41 s |
Verifique o número de linhas lidas através do parâmetroread_rowsnos logs de consultas lentas. Consulte Obter e analisar logs de consultas lentas . Para confirmar que o índice bitmap está sendo aplicado, procure pela palavra-chaveBitmap Filterno plano de execução.
Para a instrução DDL, consulte DDL do Cenário 4.
Cenário 5: Consultas de agregação em um único campo
Padrão de consulta
SELECT
l_suppkey,
sum(l_extendedprice * (1 - l_discount))
FROM
lineitem
GROUP BY
l_suppkey;
Configuração
Defina o campo do GROUP BY (l_suppkey) como chave de distribuição. Quando os dados já estão distribuídos pela chave de agregação, cada shard calcula sua agregação local independentemente, eliminando a necessidade de mover dados entre shards.
Como funciona
Sem uma chave de distribuição correspondente, o mecanismo redistribui (shuffle) todas as linhas para um conjunto de shards redutores antes de agregar. Com uma chave de distribuição correspondente, evita-se que um grande volume de dados seja movido entre os shards.
Resultados de desempenho (tabela orientada a colunas)
|
Configuração |
Dados redistribuídos |
Duração da execução |
|
|
~0,21 GB |
2,30 s |
|
Campo diferente como chave de distribuição |
~8,16 GB |
3,68 s |
Verifique o volume de dados redistribuídos através do parâmetro shuffle_bytes nos logs de consultas lentas. Consulte Obter e analisar logs de consultas lentas .
Para a instrução DDL, consulte DDL do Cenário 5.
Cenário 6: Consultas JOIN entre múltiplas tabelas
Padrão de consulta
SELECT
o_orderpriority,
count(*) AS order_count
FROM
orders
WHERE
o_orderdate >= date '1996-07-01'
AND o_orderdate < date '1996-07-01' + interval '3' month
AND EXISTS (
SELECT
*
FROM
lineitem
WHERE
l_orderkey = o_orderkey -- join condition
AND l_commitdate < l_receiptdate)
GROUP BY
o_orderpriority
ORDER BY
o_orderpriority;
Configuração
Defina os campos de JOIN como chaves de distribuição em ambas as tabelas:
Lineitem:
l_orderkeycomo chave de distribuiçãoOrders:
o_orderkeycomo chave de distribuição
Quando ambas as tabelas são distribuídas pela mesma chave, as linhas correspondentes de cada tabela já estão colocalizadas no mesmo shard. O JOIN é executado localmente, sem necessidade de redistribuir nenhuma das tabelas pela rede.
Como funciona
Se as tabelas forem distribuídas por chaves diferentes, um grande volume de dados precisará ser redistribuído entre os shards para executar o JOIN. Alinhar as chaves de distribuição com os campos de JOIN impede essa movimentação massiva de dados e transforma uma operação limitada pela rede em uma operação local.
Resultados de desempenho (ambas as tabelas orientadas a colunas)
|
Configuração |
Dados redistribuídos |
Duração da execução |
|
Campos de JOIN como chaves de distribuição em ambas as tabelas |
~0,45 GB |
2,19 s |
|
Chaves de distribuição não alinhadas com campos de JOIN |
~6,31 GB |
5,55 s |
Verifique o volume de dados redistribuídos através do parâmetro shuffle_bytes nos logs de consultas lentas. Consulte Obter e analisar logs de consultas lentas .
Para as instruções DDL, consulte DDL do Cenário 6.
(Opcional) Atribua tabelas a grupos de tabelas
Se sua instância Hologres possuir mais de 256 núcleos e atender a diversas cargas de trabalho, configure múltiplos grupos de tabelas e atribua cada tabela ao grupo apropriado no momento da criação. Isso isola o uso de recursos entre as cargas de trabalho. Para detalhes, consulte Melhores práticas para configuração de grupos de tabelas.
Apêndice: Instruções DDL
DDL do Cenário 1
-- Create the Orders table with a primary key.
-- To create the table without a primary key, remove PRIMARY KEY from the O_ORDERKEY column.
DROP TABLE IF EXISTS orders;
BEGIN;
CREATE TABLE orders(
O_ORDERKEY BIGINT NOT NULL PRIMARY KEY
,O_CUSTKEY INT NOT NULL
,O_ORDERSTATUS TEXT NOT NULL
,O_TOTALPRICE DECIMAL(15,2) NOT NULL
,O_ORDERDATE TIMESTAMPTZ NOT NULL
,O_ORDERPRIORITY TEXT NOT NULL
,O_CLERK TEXT NOT NULL
,O_SHIPPRIORITY INT NOT NULL
,O_COMMENT TEXT NOT NULL
);
CALL SET_TABLE_PROPERTY('orders', 'orientation', 'row');
CALL SET_TABLE_PROPERTY('orders', 'clustering_key', 'o_orderkey');
CALL SET_TABLE_PROPERTY('orders', 'distribution_key', 'o_orderkey');
COMMIT;
DDL do Cenário 2
-- Create the Lineitem table with properties configured for prefix scan optimization.
DROP TABLE IF EXISTS lineitem;
BEGIN;
CREATE TABLE lineitem
(
L_ORDERKEY BIGINT NOT NULL,
L_PARTKEY INT NOT NULL,
L_SUPPKEY INT NOT NULL,
L_LINENUMBER INT NOT NULL,
L_QUANTITY DECIMAL(15,2) NOT NULL,
L_EXTENDEDPRICE DECIMAL(15,2) NOT NULL,
L_DISCOUNT DECIMAL(15,2) NOT NULL,
L_TAX DECIMAL(15,2) NOT NULL,
L_RETURNFLAG TEXT NOT NULL,
L_LINESTATUS TEXT NOT NULL,
L_SHIPDATE TIMESTAMPTZ NOT NULL,
L_COMMITDATE TIMESTAMPTZ NOT NULL,
L_RECEIPTDATE TIMESTAMPTZ NOT NULL,
L_SHIPINSTRUCT TEXT NOT NULL,
L_SHIPMODE TEXT NOT NULL,
L_COMMENT TEXT NOT NULL,
PRIMARY KEY (L_ORDERKEY,L_LINENUMBER)
);
CALL set_table_property('lineitem', 'orientation', 'row');
-- CALL set_table_property('lineitem', 'clustering_key', 'L_ORDERKEY,L_SHIPDATE');
CALL set_table_property('lineitem', 'distribution_key', 'L_ORDERKEY');
COMMIT;
DDL do Cenário 3
-- Create a partitioned Lineitem table partitioned by year.
-- The non-partitioned version uses the same DDL as Scenario 2.
DROP TABLE IF EXISTS lineitem;
BEGIN;
CREATE TABLE lineitem
(
L_ORDERKEY BIGINT NOT NULL,
L_PARTKEY INT NOT NULL,
L_SUPPKEY INT NOT NULL,
L_LINENUMBER INT NOT NULL,
L_QUANTITY DECIMAL(15,2) NOT NULL,
L_EXTENDEDPRICE DECIMAL(15,2) NOT NULL,
L_DISCOUNT DECIMAL(15,2) NOT NULL,
L_TAX DECIMAL(15,2) NOT NULL,
L_RETURNFLAG TEXT NOT NULL,
L_LINESTATUS TEXT NOT NULL,
L_SHIPDATE TIMESTAMPTZ NOT NULL,
L_COMMITDATE TIMESTAMPTZ NOT NULL,
L_RECEIPTDATE TIMESTAMPTZ NOT NULL,
L_SHIPINSTRUCT TEXT NOT NULL,
L_SHIPMODE TEXT NOT NULL,
L_COMMENT TEXT NOT NULL,
L_YEAR TEXT NOT NULL,
PRIMARY KEY (L_ORDERKEY,L_LINENUMBER,L_YEAR)
)
PARTITION BY LIST (L_YEAR);
CALL set_table_property('lineitem', 'clustering_key', 'L_ORDERKEY,L_SHIPDATE');
CALL set_table_property('lineitem', 'segment_key', 'L_SHIPDATE');
CALL set_table_property('lineitem', 'distribution_key', 'L_ORDERKEY');
COMMIT;
DDL do Cenário 4
-- Create the Lineitem table without clustering key or bitmap index (baseline).
-- For the optimized version, add the clustering key and bitmap_columns properties as shown in the comments.
DROP TABLE IF EXISTS lineitem;
BEGIN;
CREATE TABLE lineitem
(
L_ORDERKEY BIGINT NOT NULL,
L_PARTKEY INT NOT NULL,
L_SUPPKEY INT NOT NULL,
L_LINENUMBER INT NOT NULL,
L_QUANTITY DECIMAL(15,2) NOT NULL,
L_EXTENDEDPRICE DECIMAL(15,2) NOT NULL,
L_DISCOUNT DECIMAL(15,2) NOT NULL,
L_TAX DECIMAL(15,2) NOT NULL,
L_RETURNFLAG TEXT NOT NULL,
L_LINESTATUS TEXT NOT NULL,
L_SHIPDATE TIMESTAMPTZ NOT NULL,
L_COMMITDATE TIMESTAMPTZ NOT NULL,
L_RECEIPTDATE TIMESTAMPTZ NOT NULL,
L_SHIPINSTRUCT TEXT NOT NULL,
L_SHIPMODE TEXT NOT NULL,
L_COMMENT TEXT NOT NULL,
PRIMARY KEY (L_ORDERKEY,L_LINENUMBER)
);
CALL set_table_property('lineitem', 'segment_key', 'L_SHIPDATE');
CALL set_table_property('lineitem', 'distribution_key', 'L_ORDERKEY');
CALL set_table_property('lineitem', 'bitmap_columns', 'l_orderkey,l_partkey,l_suppkey,l_linenumber,l_returnflag,l_linestatus,l_shipinstruct,l_comment');
COMMIT;
DDL do Cenário 5
-- Create the Lineitem table with the GROUP BY field configured as the distribution key.
DROP TABLE IF EXISTS lineitem;
BEGIN;
CREATE TABLE lineitem
(
L_ORDERKEY BIGINT NOT NULL,
L_PARTKEY INT NOT NULL,
L_SUPPKEY INT NOT NULL,
L_LINENUMBER INT NOT NULL,
L_QUANTITY DECIMAL(15,2) NOT NULL,
L_EXTENDEDPRICE DECIMAL(15,2) NOT NULL,
L_DISCOUNT DECIMAL(15,2) NOT NULL,
L_TAX DECIMAL(15,2) NOT NULL,
L_RETURNFLAG TEXT NOT NULL,
L_LINESTATUS TEXT NOT NULL,
L_SHIPDATE TIMESTAMPTZ NOT NULL,
L_COMMITDATE TIMESTAMPTZ NOT NULL,
L_RECEIPTDATE TIMESTAMPTZ NOT NULL,
L_SHIPINSTRUCT TEXT NOT NULL,
L_SHIPMODE TEXT NOT NULL,
L_COMMENT TEXT NOT NULL,
PRIMARY KEY (L_ORDERKEY,L_LINENUMBER,L_SUPPKEY)
);
CALL set_table_property('lineitem', 'segment_key', 'L_COMMITDATE');
CALL set_table_property('lineitem', 'clustering_key', 'L_ORDERKEY,L_SHIPDATE');
CALL set_table_property('lineitem', 'distribution_key', 'L_SUPPKEY');
COMMIT;
DDL do Cenário 6
DROP TABLE IF EXISTS LINEITEM;
BEGIN;
CREATE TABLE LINEITEM
(
L_ORDERKEY BIGINT NOT NULL,
L_PARTKEY INT NOT NULL,
L_SUPPKEY INT NOT NULL,
L_LINENUMBER INT NOT NULL,
L_QUANTITY DECIMAL(15,2) NOT NULL,
L_EXTENDEDPRICE DECIMAL(15,2) NOT NULL,
L_DISCOUNT DECIMAL(15,2) NOT NULL,
L_TAX DECIMAL(15,2) NOT NULL,
L_RETURNFLAG TEXT NOT NULL,
L_LINESTATUS TEXT NOT NULL,
L_SHIPDATE TIMESTAMPTZ NOT NULL,
L_COMMITDATE TIMESTAMPTZ NOT NULL,
L_RECEIPTDATE TIMESTAMPTZ NOT NULL,
L_SHIPINSTRUCT TEXT NOT NULL,
L_SHIPMODE TEXT NOT NULL,
L_COMMENT TEXT NOT NULL,
PRIMARY KEY (L_ORDERKEY,L_LINENUMBER)
);
CALL set_table_property('LINEITEM', 'clustering_key', 'L_SHIPDATE,L_ORDERKEY');
CALL set_table_property('LINEITEM', 'segment_key', 'L_SHIPDATE');
CALL set_table_property('LINEITEM', 'distribution_key', 'L_ORDERKEY');
CALL set_table_property('LINEITEM', 'bitmap_columns', 'L_ORDERKEY,L_PARTKEY,L_SUPPKEY,L_LINENUMBER,L_RETURNFLAG,L_LINESTATUS,L_SHIPINSTRUCT,L_SHIPMODE,L_COMMENT');
CALL set_table_property('LINEITEM', 'dictionary_encoding_columns', 'L_RETURNFLAG,L_LINESTATUS,L_SHIPINSTRUCT,L_SHIPMODE,L_COMMENT');
COMMIT;
DROP TABLE IF EXISTS ORDERS;
BEGIN;
CREATE TABLE ORDERS
(
O_ORDERKEY BIGINT NOT NULL PRIMARY KEY,
O_CUSTKEY INT NOT NULL,
O_ORDERSTATUS TEXT NOT NULL,
O_TOTALPRICE DECIMAL(15,2) NOT NULL,
O_ORDERDATE timestamptz NOT NULL,
O_ORDERPRIORITY TEXT NOT NULL,
O_CLERK TEXT NOT NULL,
O_SHIPPRIORITY INT NOT NULL,
O_COMMENT TEXT NOT NULL
);
CALL set_table_property('ORDERS', 'segment_key', 'O_ORDERDATE');
CALL set_table_property('ORDERS', 'distribution_key', 'O_ORDERKEY');
CALL set_table_property('ORDERS', 'bitmap_columns', 'O_ORDERKEY,O_CUSTKEY,O_ORDERSTATUS,O_ORDERPRIORITY,O_CLERK,O_SHIPPRIORITY,O_COMMENT');
CALL set_table_property('ORDERS', 'dictionary_encoding_columns', 'O_ORDERSTATUS,O_ORDERPRIORITY,O_CLERK,O_COMMENT');
COMMIT;
Referências
Para instruções de Linguagem de Definição de Dados (DDL) referentes a tabelas internas do Hologres, consulte: