Saudações, habr.
Se alguém explorar o sistema
ClickHouse resolve bem os problemas descritos. Por exemplo, depois de transferir 2TiB de dados do sussurro, eles cabem em 300GiB. Não vou me alongar na comparação em detalhes; há muitos artigos sobre esse assunto. Além disso, até recentemente, nem tudo era perfeito com nosso armazenamento ClickHouse.
Problemas com espaço consumido
À primeira vista, tudo deveria funcionar bem. Seguindo retention
), em seguida crie uma tabela de acordo com a recomendação do backend selecionado para graphite-web:
Para entender qual deles, você precisa saber como funcionam as inserções e a trajetória de vida dos dados nas tabelas de motores da família *Mesclar Árvore ClickHouse (gráficos retirados de
- inserido
блок
dados. No nosso caso, foram as métricas que chegaram.
- Cada bloco é classificado de acordo com a chave antes de ser gravado no disco.
ORDER BY
especificado ao criar a tabela. - Após a classificação,
кусок
(part
) os dados são gravados no disco.
- O servidor monitora em segundo plano para que não haja muitas dessas peças e lança em segundo plano
слияния
(merge
, doravante mesclado).
- O servidor para de executar mesclagens por conta própria assim que os dados param de fluir ativamente para o
партицию
(partition
), mas você pode iniciar o processo manualmente com o comandoOPTIMIZE
. - Se houver apenas uma parte restante na partição, você não poderá executar a mesclagem usando o comando usual; você deve usar
OPTIMIZE ... FINAL
Então, chegam as primeiras métricas. E eles ocupam algum espaço. Os eventos subsequentes podem variar um pouco dependendo de muitos fatores:
- A chave de particionamento pode ser muito pequena (um dia) ou muito grande (vários meses).
- A configuração de retenção pode caber em vários limites significativos de agregação de dados na partição ativa (onde as métricas são registradas), ou talvez não.
- Se houver muitos dados, então os primeiros pedaços, que devido à fusão em segundo plano podem já ser enormes (se você escolher uma chave de particionamento não ideal), não se fundirão com pequenos pedaços novos.
E sempre termina do mesmo jeito. O espaço ocupado pelas métricas no ClickHouse só aumenta se:
- não se aplica
OPTIMIZE ... FINAL
manualmente ou - não insira dados em todas as partições continuamente, para que mais cedo ou mais tarde uma mesclagem em segundo plano comece
O segundo método parece ser o mais fácil de implementar e, portanto, está incorreto e foi tentado primeiro.
Eu escrevi um script python bastante simples que enviou métricas fictícias para todos os dias nos últimos 4 anos e executei o cron a cada hora.
Como todo o funcionamento do ClickHouse DBMS se baseia no fato de que este sistema mais cedo ou mais tarde fará todo o trabalho em segundo plano, mas não se sabe quando, não pude esperar o momento em que as velhas peças enormes se dignassem a começar a se fundir com novos pequenos. Ficou claro que precisávamos procurar uma maneira de automatizar as otimizações forçadas.
Informações nas tabelas do sistema ClickHouse
Vamos dar uma olhada na estrutura da tabela
- nome do banco de dados (
database
); - Nome da tabela (
table
); - nome e ID da partição (
partition
&partition_id
); - quando a peça foi criada (
modification_time
); - data mínima e máxima em uma peça (o particionamento é feito por dia) (
min_date
&max_date
);
Há também uma mesa
- nome do banco de dados (
Tables.database
); - Nome da tabela (
Tables.table
); - idade da métrica quando a próxima agregação deve ser aplicada (
age
);
Assim:
- Temos uma tabela de pedaços e uma tabela de regras de agregação.
- Combinamos sua interseção e obtemos todas as tabelas *GraphiteMergeTree.
- Estamos procurando todas as partições nas quais:
- mais de uma peça
- ou chegou a hora de aplicar a próxima regra de agregação, e
modification_time
mais antigo que este momento.
Implementação
Esse pedido
SELECT
concat(p.database, '.', p.table) AS table,
p.partition_id AS partition_id,
p.partition AS partition,
-- Самое "старое" правило, которое может быть применено для
-- партиции, но не в будущем, см (*)
max(g.age) AS age,
-- Количество кусков в партиции
countDistinct(p.name) AS parts,
-- За самую старшую метрику в партиции принимается 00:00:00 следующего дня
toDateTime(max(p.max_date + 1)) AS max_time,
-- Когда партиция должна быть оптимизированна
max_time + age AS rollup_time,
-- Когда самый старый кусок в партиции был обновлён
min(p.modification_time) AS modified_at
FROM system.parts AS p
INNER JOIN
(
-- Все правила для всех таблиц *GraphiteMergeTree
SELECT
Tables.database AS database,
Tables.table AS table,
age
FROM system.graphite_retentions
ARRAY JOIN Tables
GROUP BY
database,
table,
age
) AS g ON
(p.table = g.table)
AND (p.database = g.database)
WHERE
-- Только активные куски
p.active
-- (*) И только строки, где правила аггрегации уже должны быть применены
AND ((toDateTime(p.max_date + 1) + g.age) < now())
GROUP BY
table,
partition
HAVING
-- Только партиции, которые младше момента оптимизации
(modified_at < rollup_time)
-- Или с несколькими кусками
OR (parts > 1)
ORDER BY
table ASC,
partition ASC,
age ASC
retorna cada uma das partições da tabela *GraphiteMergeTree cuja fusão deve liberar espaço em disco. A única coisa que resta a fazer é passar por todos eles com um pedido OPTIMIZE ... FINAL
. A implementação final também leva em consideração o fato de não haver necessidade de mexer em partições com gravação ativa.
Isso é exatamente o que o projeto faz
Se você executar o programa em um servidor com ClickHouse, ele simplesmente começará a funcionar no modo daemon. A cada hora será executada uma solicitação, verificando se surgiram novas partições com mais de três dias que possam ser otimizadas.
Nossos planos imediatos são fornecer pelo menos pacotes deb e, se possível, também rpm.
Em vez de uma conclusão
Nos últimos mais de 9 meses estive dentro da minha empresa
Vários litros de cerveja e dias administrativos foram gastos no desenvolvimento do pedido, juntamente com
Fonte: habr.com