Os "perrengues" depois de transformar um DB de 8TB em 218GB
OPA!
E aí, pessoal.
Continuando a minha saga sobre a migração de PostgreSQL para ClickHouse...
Pra quem não viu o começo, vai um sumário:
1: Reduzi um DB de 52 GB para 4.5 GB...
2: Reduzi um DB de 8 TB para 218 GB...
Agora vamos para o post 3.
Depois de diversos testes, recriações do DB e várias migrações repetidas, cheguei a alguns vereditos interessantes e também a algumas dificuldades.
Sem enrolação, vou descrever em tópicos alguns detalhes e dificuldades que percebi ao usar o ClickHouse.
Detalhe: ainda não coloquei em produção!
Estou testando bastante pra não dar um passo maior que a perna hahaha.
ClickHouse não tem ID auto-increment
- Não sei se isso pode ser um problema para você, mas para mim gera uma certa dificuldade em percorrer os dados de forma sequencial sem repetições. Percebi ainda mais essa dificuldade quando precisei migrar dados entre duas tabelas no próprio ClickHouse. Tive que usar uma lógica de ponteiros com base em várias colunas.
- Claro que você pode criar uma coluna de ID e inserir os valores sequenciais pela sua aplicação, ou usar um UUID autogerado pelo ClickHouse. Mas eu gosto dos IDs numéricos sequenciais das tabelas padrão kkkkk.
ClickHouse é um pouco lento para ler dados sequenciais e reinserir nele próprio
- Ao ler e inserir 1M de linhas no próprio DB (entre tabelas), percebi que o processo é mais lento do que o esperado (inclusive mais lento que ler do PostgreSQL e inserir no ClickHouse).
- Não sei se estou sendo noob, mas achei lento. Então, se eu precisar mover os dados para fora do ClickHouse futuramente, vai levar alguns dias.
- O curioso é que o processo inverso, mover de fora para dentro, é incrivelmente rápido. Usando um script otimizado em Node.js, consegui mover 44 bilhões de registros em menos de 2 dias full time.
ClickHouse é HTTP
- Não vejo isso como um problema, é apenas uma forma diferente de usar o DB.
- Normalmente conectamos a um DB via client usando protocolo TCP binário otimizado. Já no ClickHouse, por padrão, são requisições HTTP — com prós e contras:
- Contras: overhead de rede, requisições um pouco mais lentas (por conta da pilha HTTP) e tráfego maior, já que tudo vai no body da requisição.
- Prós: simplicidade. É mais fácil lidar com HTTP do que com protocolo TCP próprio. Dá pra colocar o banco atrás de um proxy reverso como Nginx, habilitar SSL/HTTPS com facilidade e aplicar proteções que seriam mais complexas em portas TCP expostas (tive ataques de brute force no PostgreSQL, por exemplo).
- O ClickHouse até possui comunicação nativa via TCP, mas não encontrei como usá-la com a lib oficial de Node.js. A documentação também não explica o porquê. Parece que o TCP nativo é mais voltado à comunicação interna. (O client de Java, por exemplo, tem suporte.)
- No fim, é apenas uma abordagem diferente para lidar com sessões no DB.
Várias engines e configurações de tabela deixam tudo meio confuso
- Confesso que isso é mais problema meu do que do DB. Esses últimos dias mexi em várias engines diferentes tentando entender qual entregava o melhor desempenho. Isso gera mais dúvidas do que respostas kkkkk.
- Mas estou conseguindo me organizar e criando vários schemas de tabelas para depois escolher a melhor opção.
- Estou gostando bastante da engine ReplacingMergeTree:
- Permite remoção de duplicados automaticamente durante merges (não durante inserts; os merges são assíncronos).
- Já ajuda bastante a mitigar duplicações de dados (que são muitas).
O ORDER BY da tabela (índice primário) é MUITO IMPORTANTE
- Se você não escolher corretamente a ordenação das colunas, suas consultas podem ficar extremamente lentas.
- Então, antes de criar a tabela e inserir bilhões de registros, descubra primeiro qual a ordenação mais usada nas consultas. Isso faz toda a diferença no desempenho.
ClickHouse sabe aproveitar todo o hardware — e isso pode ser um problema em consultas mal otimizadas
- Várias vezes testei consultas que simplesmente engoliram toda a RAM e CPU do servidor kkkkk.
- Isso é perigoso: um único cliente pode sobrecarregar o DB e prejudicar os outros.
- Resolvi isso otimizando consultas e configurando limites de CPU e tempo de execução. Assim, se a consulta ultrapassar o limite, ela é cancelada.
- Não falo disso como um problema, é mais uma peculiaridade que precisa ter atenção. O DB é bem performático mas tambem sabe usar tudo quando precisar.
- Um ponto importante: o DB não caiu nenhuma vez durante esses casos, mesmo ele comendo toda a RAM, ele mesmo soube parar e cancelar a query quando chegou nesse ponto, então não tive nenhuma falha catastrófica.
Cancelar consultas no cliente não cancela no DB
- Isso pode acontecer em outros DBs, mas no ClickHouse percebi que é quase sempre.
- Executei consultas, cancelei no DBeaver, mas elas continuaram rodando. Precisei pegar o ID e matar manualmente.
- Nesse caso, limitar CPU e tempo de execução diretamente no SQL é a melhor prática.
Para tirar o máximo do DB, é necessário usar tabelas de agregação e Materialized Views
- Consultar apenas a tabela principal não entregou o desempenho ideal.
- Com a ajuda do ChatGPT (kkkkk), criei MVs e tabelas agregadas populadas/atualizadas a cada insert. O resultado: consultas extremamente rápidas e leves.
- Um detalhe que me agradou: o ClickHouse roda essas agregações sem problemas, mesmo com milhões de inserts, e o consumo de CPU/memória é mínimo. Diferente do TimescaleDB, que em testes consumiu bastante CPU para manter MVs de agregação.
- Ou seja, dá pra usar e abusar de MVs e tabelas agregadas para máximo desempenho.
A tabela precisa ser bem planejada
- As modificações são limitadas depois que ela é criada e populada.
- Durante os testes, precisei recriar tabelas e repetir migrações, o que serviu como aprendizado.
- Para produção, no entanto, é preciso planejar bem antes de criar.
A compressão dos dados vicia! kkkkk
- Depois de ver tamanha compressão, fiquei viciado em buscar formas de reduzir ainda mais kkkkk.
Veredito
- Os pontos acima não são motivos para desconsiderar o DB. Pelo contrário: o ClickHouse é incrível, virei fã e vou continuar nessa saga de otimização para colocar em produção.
- Só espero que meu chefe não reclame da demora kkkkk. Quero testar bastante para entender bem o DB.
Se aparecerem mais pontos interessantes, posto aqui!
Obrigado a quem está acompanhando essa saga :)
Atualização:
Em um dos pontos do post falei que o processo de "mover" os dados entre tabelas (que consiste em um script fazer um select em batches e inserir em outra tabela) acaba sendo lento.
Pesquisando mais um pouco, descobri que existe uma forma de fazer isso com uma query simples:
INSERT INTO nova_tabela
SELECT col1, col2, col3, ... , ... ,
FROM tabela_antiga
e deixar rolando esse processo.
em questão de 30s ja tinha inserido 200M de linhas na tabela nova oriundas da tabela antiga. e o uso de cpu ficou em torno de 25%, e memoria em 4gb.
Tentei fazer o mesmo em outros DBs e sempre tive problemas com travamentos, então nem imaginei que isso era otimizado no clickhouse.
Mantenho a ideia de que queries sequenciais com milhoes de linhas acabam sendo meio lentas, mas esse processo interno do próprio Clickhouse de fazer um insert com um select de outra tabela é bem otimizado e já faz os inserts em batches.