O algoritmo roaring bitmap funciona bem para consultas de tags de atributos, mas apresenta limitações na análise de tags de comportamento numéricas — como GMV, visualizações de página ou valores de pedidos — em grandes grupos de usuários. Também enfrenta dificuldades com tags de alta cardinalidade, nas quais o armazenamento aumenta significativamente após a deduplicação. O Hologres oferece o algoritmo de índice bit-sliced (BSI) para superar essas limitações: o BSI pré-computa e compacta valores de tags numéricas, permitindo executar consultas de soma, distribuição e top-K sem junções com tabelas de detalhes.
Quando usar o BSI
Use o BSI quando os roaring bitmaps sozinhos não atenderem às suas necessidades:
Análise de tags de comportamento numéricas: Consultas em tags numéricas, como GMV, valor do pedido ou duração de reprodução, exigem junções com tabelas de detalhes ao usar apenas roaring bitmaps, o que aumenta a latência da consulta. O BSI pré-computa e compacta esses valores, eliminando a necessidade de junção.
Consultas de tags de alta cardinalidade: Quando uma tag possui muitos valores distintos após a deduplicação, os roaring bitmaps consomem muito armazenamento e tornam as consultas mais lentas. O BSI armazena todos os valores de tags dos usuários em no máximo 32 fatias de bits, mantendo o armazenamento compacto e as consultas rápidas.
Como o BSI funciona
O BSI codifica valores de tags numéricas em binário e distribui os IDs de usuário (UIDs) entre fatias de bits. Cada fatia corresponde a uma posição de bit na representação binária do valor da tag.
Com essa codificação:
Uma operação de soma torna-se uma interseção de bitmaps em todas as fatias.
Uma operação top-K torna-se uma interseção de bitmaps começando pelos bits de maior ordem.



O BSI combina-se com operações de roaring bitmap (AND, OR, NOT) para permitir análises de associação rápidas entre tags de atributos e tags de comportamento.


Para obter a referência completa de funções, consulte Funções BSI.
Observações de uso
Requisito de tipo de UID: O BSI exige valores de UID do tipo
INT. Para UIDs não inteiros, crie uma tabela de codificação de dicionário (comodws_uid_dict) para mapear UIDs para valores inteirosencode_uidantes de criar os dados BSI.Distribuição de recursos: Sem bucketing, as tabelas BSI e roaring bitmap ficam em nós específicos, deixando outros recursos da instância ociosos. Para cargas de trabalho de produção com grandes conjuntos de dados, use a abordagem de bucketing descrita em Práticas avançadas: bucketing.
Agregação entre buckets: A função
bsi_add_aggagrega dados BSI em vários buckets antes de aplicarbsi_topkoubsi_stat. Use-a sempre em consultas com bucketing.
Práticas básicas
Este exemplo usa duas tabelas source:
dws_userbase: tags de atributos do usuário (província, gênero)usershop_behavior: tags de comportamento do usuário (GMV = Volume Bruto de Mercadorias)
Configurar tabelas
|
Tabela |
Campos |
Descrição |
|
|
|
Tabela source de tags de atributos (igual à solução de tabela wide) |
|
|
|
Tabela de codificação de dicionário de UID (igual à solução de roaring bitmap) |
|
|
|
Tabela source de tags de comportamento |
|
|
|
Tabela de tags de atributos criada com roaring bitmap |
|
|
|
Tabela de métricas GMV criada com BSI |
Crie as tabelas com as seguintes instruções de Linguagem de Definição de Dados (DDL):
CREATE TABLE dws_userbase (
uid int NOT NULL PRIMARY KEY,
province text,
gender text
... -- Other attribute columns.
)
WITH (
distribution_key = 'uid'
);
CREATE TABLE dws_uid_dict (
encode_uid serial,
uid int PRIMARY KEY
);
CREATE TABLE usershop_behavior (
uid int NOT NULL,
gmv int
)
WITH (
distribution_key = 'uid'
);
CREATE TABLE rb_tag (
tag_name text,
tag_val text,
bitmap roaringbitmap
);
CREATE TABLE bsi_gmv (
gmv_bsi bsi
);
Carregar dados
Passo 1. Crie roaring bitmaps para tags de atributos (província e gênero) a partir de dws_userbase e dws_uid_dict, e insira-os em rb_tag.
INSERT INTO rb_tag
SELECT
'province',
province,
rb_build_agg(b.encode_uid) AS bitmap
FROM
dws_userbase a
JOIN dws_uid_dict b ON a.uid = b.uid
GROUP BY
province;
INSERT INTO rb_tag
SELECT
'gender',
gender,
rb_build_agg(b.encode_uid) AS bitmap
FROM
dws_userbase a
JOIN dws_uid_dict b ON a.uid = b.uid
GROUP BY
gender;
Passo 2. Crie dados BSI para tags de comportamento GMV a partir de usershop_behavior e dws_uid_dict, e insira-os em bsi_gmv.
INSERT INTO bsi_gmv
SELECT
bsi_build(array_agg(b.encode_uid), array_agg(a.gmv)) AS bitmap
FROM
usershop_behavior a
JOIN dws_uid_dict b ON a.uid = b.uid;
Executar análise de perfil
Use o BSI com operações de roaring bitmap para analisar tags de comportamento de grupos de usuários identificados. As consultas a seguir têm como alvo usuários do sexo masculino da província de Guangdong. Cada consulta mostra a abordagem BSI juntamente com o SQL simples equivalente para comparação.
Analisar tags de comportamento para um grupo de usuários
Consultar GMV total e GMV médio
Abordagem BSI:
SELECT
sum(kv[1]) AS total_gmv, -- Total GMV.
sum(kv[1])/sum(kv[2]) AS avg_gmv -- Average GMV.
FROM (
SELECT
bsi_sum(t1.gmv_bsi, t2.crowd) AS kv
FROM
bsi_gmv t1,
(SELECT rb_and(a.bitmap, b.bitmap) AS crowd FROM
(SELECT bitmap FROM rb_tag WHERE tag_name = 'gender' AND tag_val = 'Male ') a, -- Male users.
(SELECT bitmap FROM rb_tag WHERE tag_name = 'province' AND tag_val = 'Guangdong') b -- Users from Guangdong province.
) t2
) t;
SQL simples:
SELECT
sum(b.gmv) AS total_gmv,
avg(b.gmv) AS avg_gmv
FROM
dws_userbase a
JOIN usershop_behavior b ON a.uid = b.uid
WHERE
a.province = 'Guangdong'
AND a.gender = 'Male';
Consultar distribuição de GMV
Abordagem BSI — use bsi_stat com valores de limite {100, 300, 500} para calcular a distribuição em vários intervalos em uma única passagem:
SELECT
bsi_stat('{100,300,500}', filter_bsi)
FROM (
SELECT
bsi_filter(t1.gmv_bsi, t2.crowd) AS filter_bsi
FROM
bsi_gmv t1,
(SELECT rb_and(a.bitmap, b.bitmap) AS crowd FROM
(SELECT bitmap FROM rb_tag WHERE tag_name = 'gender' AND tag_val = 'Male ') a, -- Male users.
(SELECT bitmap FROM rb_tag WHERE tag_name = 'province' AND tag_val = 'Guangdong') b -- Users from Guangdong province.
) t2
) t;
SQL simples — requer uma expressão CASE WHEN para cada intervalo:
SELECT
CASE
WHEN gmv >= 0 AND gmv <= 100 THEN '0-100'
WHEN gmv > 100 AND gmv <= 300 THEN '100-300'
WHEN gmv > 300 AND gmv <= 500 THEN '300-500'
WHEN gmv > 500 THEN '>500'
END AS gmv_range,
COUNT(*) AS user_count
FROM
dws_userbase a
JOIN usershop_behavior b ON a.uid = b.uid
WHERE a.province = 'Guangdong'
AND a.gender = 'Male'
GROUP BY gmv_range
ORDER BY gmv_range;
Consultar os K maiores valores de GMV
Abordagem BSI:
SELECT
rb_to_array(bsi_topk(filter_bsi, 10))
FROM (
SELECT
bsi_filter(t1.gmv_bsi, t2.crowd) AS filter_bsi
FROM
bsi_gmv t1,
(SELECT rb_and(a.bitmap, b.bitmap) AS crowd FROM
(SELECT bitmap FROM rb_tag WHERE tag_name = 'gender' AND tag_val = 'Male ') a, -- Male users.
(SELECT bitmap FROM rb_tag WHERE tag_name = 'province' AND tag_val = 'Guangdong') b -- Users from Guangdong province.
) t2
) t;
SQL simples:
SELECT
b.uid,
b.gmv
FROM
dws_userbase a
JOIN usershop_behavior b ON a.uid = b.uid
WHERE
a.province = 'Guangdong'
AND a.gender = 'Male'
ORDER BY gmv DESC
LIMIT 10;
Filtrar usuários por tag de comportamento
Identificar usuários cujo GMV excede 1.000
Abordagem BSI:
SELECT
rb_to_array(bsi_gt(gmv_bsi, 1000)) AS crowd
FROM
bsi_gmv;
SQL simples:
SELECT
array_agg(uid)
FROM
usershop_behavior
WHERE
gmv > 800;
Práticas avançadas: bucketing
Sem bucketing, as tabelas BSI e roaring bitmap ficam concentradas em nós específicos, deixando o restante da instância subutilizado. Dividir essas tabelas em 65.536 segmentos distribui os dados por todos os nós, aumentando a concorrência de consultas e a eficiência dos recursos.
A fórmula de atribuição de bucket é encode_uid / 65536 AS bucket. Tanto rb_tag quanto bsi_gmv usam bucket como chave de distribuição para garantir que segmentos correspondentes de cada tabela fiquem sempre colocalizados no mesmo nó.
Configurar tabelas
Em comparação com a configuração básica, a configuração avançada adiciona uma coluna bucket a rb_tag e bsi_gmv, além de adicionar colunas category e ds a bsi_gmv para consultas particionadas por tempo e nível de categoria.
|
Tabela |
Campos |
Descrição |
|
|
|
Igual às práticas básicas |
|
|
|
Igual às práticas básicas |
|
|
|
Adiciona |
|
|
|
Adiciona |
|
|
|
Adiciona |
CREATE TABLE rb_tag (
tag_name text,
tag_val text,
bucket int,
bitmap roaringbitmap
)
WITH (
distribution_key = 'bucket' -- Use the bucket ID as the distribution key.
);
CREATE TABLE bsi_gmv (
category text,
bucket int,
gmv_bsi bsi,
ds date
)
WITH (
distribution_key = 'bucket' -- Use the bucket ID as the distribution key.
);
Carregar dados
Passo 1. Crie roaring bitmaps para tags de atributos, particionados em buckets.
INSERT INTO rb_tag
SELECT
'province',
province,
encode_uid / 65536 AS "bucket",
rb_build_agg(b.encode_uid) AS bitmap
FROM
dws_userbase a
JOIN dws_uid_dict b ON a.uid = b.uid
GROUP BY
province,
"bucket";
INSERT INTO rb_tag
SELECT
'gender',
gender,
encode_uid / 65536 AS "bucket",
rb_build_agg(b.encode_uid) AS bitmap
FROM
dws_userbase a
JOIN dws_uid_dict b ON a.uid = b.uid
GROUP BY
gender,
"bucket";
Passo 2. Crie dados BSI para GMV, particionados em buckets. Execute este passo diariamente para carregar os dados do dia anterior.
INSERT INTO bsi_gmv
SELECT
a.category,
b.encode_uid / 65536 AS "bucket",
bsi_build(array_agg(b.encode_uid), array_agg(a.gmv)) AS bitmap,
a.ds
FROM
usershop_behavior a
JOIN dws_uid_dict b ON a.uid = b.uid
WHERE
ds = CURRENT_DATE - interval '1 day'
GROUP BY
category,
"bucket",
ds;
Executar análise de perfil
Todas as consultas abaixo fazem JOIN entre bsi_gmv e rb_tag em bucket para que o mecanismo processe segmentos correspondentes juntos em cada nó.
Analisar tags de comportamento para um grupo de usuários
Consultar GMV total e GMV médio para usuários do sexo masculino de Guangdong na categoria 3C (dia anterior)
SELECT
sum(kv[1]) AS total_gmv, -- Total GMV.
sum(kv[1])/sum(kv[2]) AS avg_gmv -- Average GMV.
FROM (
SELECT
bsi_sum(t1.gmv_bsi, t2.crowd) AS kv,
t1.bucket
FROM
(SELECT gmv_bsi, bucket FROM bsi_gmv WHERE category = '3C' AND ds = CURRENT_DATE - interval '1 day') t1
JOIN
(SELECT rb_and(a.bitmap, b.bitmap) AS crowd, a.bucket FROM
(SELECT bitmap, bucket FROM rb_tag WHERE tag_name = 'gender' AND tag_val = 'Male ') a -- Male users.
JOIN
(SELECT bitmap, bucket FROM rb_tag WHERE tag_name = 'province' AND tag_val = 'Guangdong') b -- Users from Guangdong province.
ON a.bucket = b.bucket
) t2
ON t1.bucket = t2.bucket
) t;
Consultar distribuição de GMV para usuários do sexo masculino de Guangdong na categoria 3C (dia anterior)
SELECT
bsi_stat('{100,300,500}', bsi_add_agg(filter_bsi))
FROM (
SELECT
bsi_filter(t1.gmv_bsi, t2.crowd) AS filter_bsi,
t1.bucket
FROM
(SELECT gmv_bsi, bucket FROM bsi_gmv WHERE category = '3C' AND ds = CURRENT_DATE - interval '1 day') t1
JOIN
(SELECT rb_and(a.bitmap, b.bitmap) AS crowd, a.bucket FROM
(SELECT bitmap, bucket FROM rb_tag WHERE tag_name = 'gender' AND tag_val = 'Male ') a -- Male users.
JOIN
(SELECT bitmap, bucket FROM rb_tag WHERE tag_name = 'province' AND tag_val = 'Guangdong') b -- Users from Guangdong province.
ON a.bucket = b.bucket
) t2
ON t1.bucket = t2.bucket
) t;
Consultar os 10 maiores valores de GMV para usuários do sexo masculino de Guangdong (dia anterior, todas as categorias)
SELECT
bsi_topk(bsi_add_agg(filter_bsi), 10)
FROM (
SELECT
bsi_filter(t1.gmv_bsi, t2.crowd) AS filter_bsi,
t1.bucket
FROM
(SELECT bsi_add_agg(gmv_bsi) AS gmv_bsi, bucket FROM bsi_gmv WHERE ds = CURRENT_DATE - interval '1 day' GROUP BY bucket) t1
JOIN
(SELECT rb_and(a.bitmap, b.bitmap) AS crowd, a.bucket FROM
(SELECT bitmap, bucket FROM rb_tag WHERE tag_name = 'gender' AND tag_val = 'Male ') a -- Male users.
JOIN
(SELECT bitmap, bucket FROM rb_tag WHERE tag_name = 'province' AND tag_val = 'Guangdong') b -- Users from Guangdong province.
ON a.bucket = b.bucket
) t2
ON t1.bucket = t2.bucket
) t;
Filtrar usuários por tag de comportamento
Identificar usuários cujo GMV excede 1.000 na categoria 3C nos últimos 30 dias
SELECT
rb_to_array(bsi_gt(bsi_add_agg(gmv_bsi), 1000)) AS crowd
FROM
bsi_gmv
WHERE
category = '3C'
AND ds BETWEEN CURRENT_DATE - interval '30 day' AND CURRENT_DATE - interval '1 day';
Próximos passos
Funções BSI — Referência completa para todas as funções BSI, incluindo sintaxe, argumentos e tipos de retorno.