Este motor:
- Permite gravar rapidamente estados de objetos que mudam continuamente.
- Exclui estados antigos de objetos em segundo plano. Isso reduz significativamente o volume de armazenamento.
Consulte a seção colapsamento para mais detalhes.
O motor herda de MergeTree e adiciona a lógica de colapsamento de linhas ao algoritmo de mesclagem de partes de dados. VersionedCollapsingMergeTree tem a mesma finalidade que CollapsingMergeTree, mas usa um algoritmo de colapsamento diferente que permite inserir dados em qualquer ordem com várias threads. Em particular, a coluna Version ajuda a colapsar as linhas corretamente, mesmo que sejam inseridas fora de ordem. Em contraste, CollapsingMergeTree permite apenas inserção estritamente consecutiva.
Criando uma tabela
CREATE TABLE [IF NOT EXISTS] [db.]table_name [ON CLUSTER cluster]
(
name1 [type1] [DEFAULT|MATERIALIZED|ALIAS expr1],
name2 [type2] [DEFAULT|MATERIALIZED|ALIAS expr2],
...
) ENGINE = VersionedCollapsingMergeTree(sign, version)
[PARTITION BY expr]
[ORDER BY expr]
[SAMPLE BY expr]
[SETTINGS name=value, ...]Para obter uma descrição dos parâmetros de consulta, consulte a descrição da consulta.
Parâmetros do motor
VersionedCollapsingMergeTree(sign, version)| Parâmetro | Descrição | Tipo |
|---|---|---|
sign |
Nome da coluna com o tipo de linha: 1 é uma linha de "estado", -1 é uma linha de "cancelamento". |
Int8 |
version |
Nome da coluna com a versão do estado do objeto. | Int*, UInt*, Date, Date32, DateTime ou DateTime64 |
Cláusulas da consulta
Ao criar uma tabela VersionedCollapsingMergeTree, são necessárias as mesmas cláusulas exigidas na criação de uma tabela MergeTree.
Método obsoleto de criação de tabela
CREATE TABLE [IF NOT EXISTS] [db.]table_name [ON CLUSTER cluster]
(
name1 [type1] [DEFAULT|MATERIALIZED|ALIAS expr1],
name2 [type2] [DEFAULT|MATERIALIZED|ALIAS expr2],
...
) ENGINE [=] VersionedCollapsingMergeTree(date-column [, sampling_expression], (primary, key), index_granularity, sign, version)Todos os parâmetros, exceto sign e version, têm o mesmo significado que em MergeTree.
-
sign— Nome da coluna que indica o tipo da linha:1é uma linha de "estado",-1é uma linha de "cancelamento".Tipo de dados da coluna —
Int8. -
version— Nome da coluna com a versão do estado do objeto.O tipo de dados da coluna deve ser
UInt*.
colapsamento
Dados
Considere uma situação em que você precisa salvar dados que mudam continuamente de um objeto. É natural ter uma linha para um objeto e atualizar essa linha sempre que houver alterações. No entanto, a operação de atualização é cara e lenta para um SGBD, porque exige reescrever os dados no armazenamento. A atualização não é aceitável se você precisa gravar dados rapidamente, mas pode gravar as alterações de um objeto de forma sequencial, como a seguir.
Use a coluna Sign ao gravar a linha. Se Sign = 1, isso significa que a linha representa um estado de um objeto (vamos chamá-la de linha de "estado"). Se Sign = -1, isso indica o cancelamento do estado de um objeto com os mesmos atributos (vamos chamá-la de linha de "cancelamento"). Use também a coluna Version, que deve identificar cada estado de um objeto com um número distinto.
Por exemplo, queremos calcular quantas páginas os usuários visitaram em um site e por quanto tempo permaneceram nele. Em determinado momento, gravamos a seguinte linha com o estado da atividade do usuário:
┌──────────────UserID─┬─PageViews─┬─Duration─┬─Sign─┬─Version─┐
│ 4324182021466249494 │ 5 │ 146 │ 1 │ 1 |
└─────────────────────┴───────────┴──────────┴──────┴─────────┘Em algum momento depois, registramos a alteração na atividade do usuário nas duas linhas a seguir.
┌──────────────UserID─┬─PageViews─┬─Duration─┬─Sign─┬─Version─┐
│ 4324182021466249494 │ 5 │ 146 │ -1 │ 1 |
│ 4324182021466249494 │ 6 │ 185 │ 1 │ 2 |
└─────────────────────┴───────────┴──────────┴──────┴─────────┘A primeira linha cancela o estado anterior do objeto (usuário). Ela deve copiar todos os campos do estado cancelado, exceto Sign.
A segunda linha contém o estado atual.
Como precisamos apenas do último estado da atividade do usuário, as linhas
┌──────────────UserID─┬─PageViews─┬─Duration─┬─Sign─┬─Version─┐
│ 4324182021466249494 │ 5 │ 146 │ 1 │ 1 |
│ 4324182021466249494 │ 5 │ 146 │ -1 │ 1 |
└─────────────────────┴───────────┴──────────┴──────┴─────────┘pode ser excluída, colapsando o estado inválido (antigo) do objeto. VersionedCollapsingMergeTree faz isso durante a mesclagem das partes de dados.
Para descobrir por que precisamos de duas linhas para cada alteração, consulte Algorithm.
Observações sobre o uso
- O programa que grava os dados deve manter o estado de um objeto na memória para poder cancelá-lo. A string "Cancel" deve conter cópias dos campos da chave primária, da versão da string "state" e o
Signoposto. Isso aumenta o tamanho inicial do armazenamento, mas permite gravar os dados rapidamente. - Arrays longos e crescentes em colunas reduzem a eficiência do motor devido à carga de gravação. Quanto mais simples forem os dados, maior será a eficiência.
- Os resultados de
SELECTdependem fortemente da consistência do histórico de alterações do objeto. Tenha cuidado ao preparar os dados para inserção. Dados inconsistentes podem produzir resultados imprevisíveis, como valores negativos para métricas não negativas, como a profundidade da sessão.
Algoritmo
Quando o ClickHouse faz o merge das partes de dados, ele exclui cada par de linhas que tenha a mesma chave primária e a mesma versão, mas Sign diferente. A ordem das linhas não importa.
Quando o ClickHouse insere dados, ele ordena as linhas pela chave primária. Se a coluna Version não estiver na chave primária, o ClickHouse a adiciona implicitamente a ela como o último campo e a usa para ordenação.
Seleção de dados
O ClickHouse não garante que todas as linhas com a mesma chave primária ficarão na mesma parte de dados resultante, nem sequer no mesmo servidor físico. Isso vale tanto para a gravação dos dados quanto para a mesclagem posterior das partes de dados. Além disso, o ClickHouse processa consultas SELECT com várias threads e não consegue prever a ordem das linhas no resultado. Isso significa que a agregação é necessária quando for preciso obter dados totalmente "colapsados" de uma tabela VersionedCollapsingMergeTree.
Para concluir o colapsamento, escreva uma consulta com uma cláusula GROUP BY e funções de agregação que levem em conta o sinal. Por exemplo, para calcular a quantidade, use sum(Sign) em vez de count(). Para calcular a soma de algum valor, use sum(Sign * x) em vez de sum(x) e adicione HAVING sum(Sign) > 0.
Os agregados count, sum e avg podem ser calculados dessa forma. O agregado uniq pode ser calculado se um objeto tiver pelo menos um estado não colapsado. Os agregados min e max não podem ser calculados porque o VersionedCollapsingMergeTree não salva o histórico dos valores dos estados colapsados.
Se você precisar extrair os dados com "colapsamento", mas sem agregação (por exemplo, para verificar se há linhas cujos valores mais recentes correspondem a determinadas condições), poderá usar o modificador FINAL na cláusula FROM. Essa abordagem é ineficiente e não deve ser usada com tabelas grandes.
Exemplo de uso
Dados de exemplo:
┌──────────────UserID─┬─PageViews─┬─Duration─┬─Sign─┬─Version─┐
│ 4324182021466249494 │ 5 │ 146 │ 1 │ 1 |
│ 4324182021466249494 │ 5 │ 146 │ -1 │ 1 |
│ 4324182021466249494 │ 6 │ 185 │ 1 │ 2 |
└─────────────────────┴───────────┴──────────┴──────┴─────────┘Criando a tabela:
CREATE TABLE UAct
(
UserID UInt64,
PageViews UInt8,
Duration UInt8,
Sign Int8,
Version UInt8
)
ENGINE = VersionedCollapsingMergeTree(Sign, Version)
ORDER BY UserIDInserção dos dados:
INSERT INTO UAct VALUES (4324182021466249494, 5, 146, 1, 1)INSERT INTO UAct VALUES (4324182021466249494, 5, 146, -1, 1),(4324182021466249494, 6, 185, 1, 2)Usamos duas consultas INSERT para criar duas partes de dados diferentes. Se inserirmos os dados em uma única consulta, o ClickHouse criará apenas uma parte de dados e nunca fará nenhum merge.
Obtendo os dados:
SELECT * FROM UAct┌──────────────UserID─┬─PageViews─┬─Duration─┬─Sign─┬─Version─┐
│ 4324182021466249494 │ 5 │ 146 │ 1 │ 1 │
└─────────────────────┴───────────┴──────────┴──────┴─────────┘
┌──────────────UserID─┬─PageViews─┬─Duration─┬─Sign─┬─Version─┐
│ 4324182021466249494 │ 5 │ 146 │ -1 │ 1 │
│ 4324182021466249494 │ 6 │ 185 │ 1 │ 2 │
└─────────────────────┴───────────┴──────────┴──────┴─────────┘O que vemos aqui e onde estão as partes colapsadas?
Criamos duas partes de dados usando duas consultas INSERT. A consulta SELECT foi executada em duas threads, e o resultado é uma ordem aleatória das linhas.
O colapsamento não ocorreu porque as partes de dados ainda não foram mescladas. O ClickHouse mescla partes de dados em algum momento imprevisível, que não podemos prever.
É por isso que precisamos de agregação:
SELECT
UserID,
sum(PageViews * Sign) AS PageViews,
sum(Duration * Sign) AS Duration,
Version
FROM UAct
GROUP BY UserID, Version
HAVING sum(Sign) > 0┌──────────────UserID─┬─PageViews─┬─Duration─┬─Version─┐
│ 4324182021466249494 │ 6 │ 185 │ 2 │
└─────────────────────┴───────────┴──────────┴─────────┘Se não precisarmos de agregação e quisermos forçar o colapsamento, podemos usar o modificador FINAL na cláusula FROM.
SELECT * FROM UAct FINAL┌──────────────UserID─┬─PageViews─┬─Duration─┬─Sign─┬─Version─┐
│ 4324182021466249494 │ 6 │ 185 │ 1 │ 2 │
└─────────────────────┴───────────┴──────────┴──────┴─────────┘Esta é uma forma muito ineficiente de selecionar dados. Não a use em tabelas grandes.