
Por um longo tempo, o iFunny usou o Redshift como um banco de dados para eventos que ocorrem em serviços de back-end e aplicativos móveis. Foi escolhido porque, no momento da implementação, em geral, não havia alternativas comparáveis em custo e conveniência.
No entanto, tudo mudou após o lançamento público do ClickHouse. Estudamos por um longo tempo, comparamos o custo, estimamos a arquitetura aproximada e, finalmente, neste verão, decidimos ver o quanto é útil para nós. Neste artigo, você aprenderá sobre o problema que o Redshift nos ajudou a resolver e como mudamos essa solução para o ClickHouse.
O problema
O iFunny precisava de um serviço semelhante ao Yandex.Metrica, mas exclusivamente para consumo doméstico. Eu vou explicar o porquê.
Clientes externos gravam eventos. Pode ser aplicativos móveis, sites ou serviços internos de back-end. É muito difícil para esses clientes explicar que o serviço de recepção de eventos não está disponível no momento, "tente enviá-lo em 15 minutos ou em uma hora". Existem muitos clientes, eles desejam enviar eventos o tempo todo e mal podem esperar.
Em contraste com eles, existem serviços e usuários internos que são bastante tolerantes a esse respeito: eles podem funcionar corretamente, mesmo com um serviço de análise inacessível. E a maioria das métricas de produtos e os resultados dos testes A / B geralmente fazem sentido assistir apenas uma vez por dia, ou talvez com menos frequência. Portanto, os requisitos de leitura são bastante baixos. No caso de um acidente ou atualização, podemos nos permitir ficar inacessíveis ou inconsistentes na leitura por várias horas ou até dias (em um caso particularmente negligenciado).
Se falamos de números, precisamos realizar cerca de cinco bilhões de eventos (300 GB de dados compactados) por dia, enquanto os armazenamos por três meses em um formulário "quente" disponível para consultas SQL e no "frio" por dois anos ou mais, mas para que, dentro de alguns dias, possamos transformá-los em "quentes".
Basicamente, os dados são uma coleção de eventos ordenados por tempo. Existem cerca de trezentos tipos de eventos, cada um com seu próprio conjunto de propriedades. Ainda existem alguns dados de fontes de terceiros que devem ser sincronizados com o banco de dados de análise: por exemplo, uma coleção de instalações de aplicativos do MongoDB ou um serviço externo do AppsFlyer.
Acontece que para o banco de dados precisamos de cerca de 40 TB de disco e, para armazenamento "frio" - cerca de 250 TB a mais.
Solução Redshift

Portanto, existem clientes móveis e serviços de back-end dos quais você precisa para receber eventos. O serviço HTTP aceita os dados, realiza a validação mínima, coleta eventos no disco local em arquivos agrupados por um minuto, os compacta imediatamente e os envia para o bucket S3. A disponibilidade desse serviço depende da disponibilidade dos servidores com o aplicativo e o AWS S3. Os aplicativos não armazenam estado, portanto, são facilmente equilibrados, dimensionados e intercambiados. O S3 é um serviço de armazenamento de arquivos relativamente simples, com boa reputação e disponibilidade, para que você possa confiar nele.
Em seguida, você precisa fornecer os dados para o Redshift. Tudo é bem simples aqui: o Redshift possui um importador S3 embutido, que é a maneira recomendada de carregar dados. Portanto, a cada 10 minutos, inicia um script que se conecta ao Redshift e solicita que ele baixe os dados usando o prefixo
s3://events-bucket/main/year=2018/month=10/day=14/10_3*
Para monitorar o status da tarefa de download, usamos o
Apache Airflow : permite repetir a operação em caso de erros e ter um histórico de execução claro, o que é importante para um grande número de tarefas. E em caso de problemas, você pode repetir o download por alguns intervalos de tempo ou baixar os dados "frios" do S3 há um ano.
No mesmo fluxo de ar, da mesma maneira, de acordo com a programação, os scripts funcionam que se conectam ao banco de dados e executam downloads periódicos de repositórios externos, ou constroem agregações sobre eventos na forma de
INSERT INTO ... SELECT ...
O desvio para o vermelho tem fracas garantias de disponibilidade. Uma vez por semana, por até meia hora (a janela de tempo é especificada nas configurações) A AWS pode impedir que o cluster atualize ou qualquer outro trabalho agendado. No caso de uma falha em um nó, o cluster também fica indisponível até que o host seja restaurado. Isso geralmente leva cerca de 15 minutos e acontece uma vez a cada seis meses. No sistema atual, isso não é um problema, ele foi originalmente projetado para que a base fique periodicamente indisponível.
No Redshift, foram usadas 4 instâncias ds2.8xlarge (36 CPU, 16 TB HDD), o que totaliza 64 TB de espaço em disco.
O último ponto é o backup. O agendamento de backup pode ser especificado nas configurações do cluster e funciona bem.
Motivação de transição da ClickHouse
Obviamente, se não houvesse problemas, ninguém teria pensado em migrar para o ClickHouse. No entanto, eles eram.
Se você observar o esquema de armazenamento ClickHouse com o mecanismo MergeTree e Redshift, poderá ver que a ideologia deles é muito semelhante. Ambos os bancos de dados são colunares, funcionam bem com um grande número de colunas e compactam muito bem os dados no disco (e no Redshift você pode configurar os tipos de compactação para cada coluna individual). Até os dados são armazenados da mesma maneira: eles são classificados por chave primária, o que permite ler apenas blocos específicos e não manter índices individuais na memória, e isso é importante ao trabalhar com grandes quantidades de dados.
A diferença essencial, como sempre, está nos detalhes.
Mesa diária
A classificação dos dados no disco e a exclusão deles no Redshift ocorre quando você faz:
VACUUM <tablename>
Nesse caso, o processo de vácuo trabalha com todos os dados nesta tabela. Se você armazenar dados dos três meses em uma tabela, esse processo levará um tempo indecente e será necessário executá-lo pelo menos diariamente, porque os dados antigos são excluídos e os novos são adicionados. Eu tive que criar tabelas separadas para cada dia e combiná-las através da Visualização, e isso não é apenas a dificuldade de rotacionar e dar suporte a essa Visualização, mas também a desaceleração das consultas. Mediante solicitação, julgando por explicação, todas as tabelas foram digitalizadas. E, embora a varredura de uma tabela leve menos de um segundo, com uma quantidade de 90 partes, qualquer consulta leva pelo menos um minuto. Isso não é muito conveniente.
Duplicatas
O próximo problema é duplicado. De uma forma ou de outra, ao transmitir dados pela rede, existem duas opções: perder dados ou receber duplicatas. Como não pudemos perder mensagens, simplesmente nos reconciliamos com o fato de que uma pequena porcentagem de eventos seria duplicada. Você pode excluir duplicatas por dia criando uma nova tabela, inserindo dados da antiga, onde as linhas da função de janela com identificação duplicada são excluídas, a tabela antiga é excluída e a nova é renomeada. Como havia uma exibição em cima das tabelas diárias, era necessário não esquecê-la e excluí-la para a hora de renomear as tabelas. Nesse caso, também era necessário monitorar os bloqueios; caso contrário, no caso de uma consulta que bloqueasse a exibição ou uma das tabelas, esse processo poderia ser arrastado por um longo tempo.
Monitoramento e manutenção
Nem uma única solicitação no Redshift leva menos de alguns segundos. Mesmo se você quiser apenas adicionar um usuário ou ver uma lista de solicitações ativas, precisará aguardar algumas dezenas de segundos. Obviamente, você pode tolerar e, para essa classe de bancos de dados, isso é aceitável, mas no final isso se traduz em um monte de tempo perdido.
Custo
De acordo com nossos cálculos, a implantação de ClickHouse nas instâncias da AWS com exatamente os mesmos recursos é exatamente metade do preço. Obviamente, deve ser assim, porque, usando o Redshift, você obtém um banco de dados pronto para conectar-se a qualquer cliente PostgreSQL logo após clicar em alguns botões no console da AWS, e a AWS fará o resto por você. Mas vale a pena? Já temos a infraestrutura, parecemos capazes de fazer backups, monitoramento e configuração, e fazemos isso para vários serviços internos. Por que não enfrentar o suporte ao ClickHouse?
Processo de transição
Primeiro, criamos uma pequena instalação ClickHouse em uma máquina, onde começamos periodicamente, usando as ferramentas internas, a baixar dados do S3. Assim, pudemos testar nossas suposições sobre a velocidade e os recursos do ClickHouse.
Após algumas semanas de teste em uma cópia pequena dos dados, ficou claro que, para substituir o Redshift pelo Clickhouse, vários problemas precisariam ser resolvidos:
- em que tipos de instâncias e discos implantar;
- usar replicação?
- como instalar, configurar e executar;
- como fazer monitoramento;
- que tipo de esquema será;
- como entregar dados do S3;
- Como reescrever todas as consultas do SQL padrão para não padrão?
Tipos de instâncias e discos . No número de processadores, disco e memória, eles decidiram desenvolver a instalação atual do Redshift. Havia várias opções, incluindo instâncias i3 com discos NVMe locais, mas decidiu parar em r5.4xlarge e armazenamento na forma de 8T ST1 EBS para cada instância. Segundo estimativas, isso deveria ter proporcionado desempenho comparável ao Redshift pela metade do custo. Ao mesmo tempo, devido ao uso de discos EBS, obtemos backups simples e recuperação através de instantâneos de discos, quase como no Redshift.
Replicação . Desde que começamos do que já está no Redshift, decidimos não usar a replicação. Além disso, isso não nos obriga a estudar imediatamente o ZooKeeper, que ainda não está na infraestrutura, mas é ótimo que agora seja possível fazer replicação sob demanda.
Instalação Esta é a parte mais fácil. Uma função suficientemente pequena para o Ansible, que instala pacotes RPM prontos e faz a mesma configuração em cada host.
Monitoramento Para monitorar todos os serviços, o Prometheus é usado junto com o Telegraf e o Grafana; portanto, eles simplesmente colocam os agentes do Telegraf nos hosts do ClickHouse, coletam um painel no Grafana, que mostra a carga atual do servidor por processador, memória e discos. Por meio do plug-in da Grafana, trouxemos para esse painel as solicitações ativas atuais para o cluster, o status das importações do S3 e outras coisas úteis. Ficou ainda melhor e mais informativo (e significativamente mais rápido) do que o painel que dava ao console da AWS.
Esquema . Um dos nossos principais erros no Redshift foi colocar apenas os campos do evento principal em colunas separadas e adicionar os campos que raramente são usados para adicionar
em uma grande coluna de propriedades. Por um lado, isso nos deu flexibilidade para alterar os campos nos estágios iniciais, quando não havia um entendimento completo de exatamente quais eventos coletaríamos, com quais propriedades, além disso, eles mudavam 5 vezes por dia. Por outro lado, os pedidos de uma grande coluna de propriedades levavam cada vez mais tempo. No ClickHouse, decidimos fazer a coisa certa imediatamente, então coletamos todas as colunas possíveis e inserimos o tipo ideal para elas. O resultado é uma tabela com aproximadamente duzentas colunas.
A próxima tarefa foi escolher o mecanismo certo para armazenamento e particionamento.
Eles não pensaram em particionar novamente, mas fizeram o mesmo que no Redshift - uma partição para cada dia, mas agora todas as partições são uma tabela, que
acelera significativamente as solicitações e simplifica a manutenção. O mecanismo de armazenamento foi utilizado pelo ReplacingMergeTree, pois permite remover duplicatas de uma partição específica, simplesmente executando
OPTIMIZE ... FINAL . Além disso, o esquema de particionamento diário permite, em caso de erros ou acidentes, trabalhar apenas com dados de um dia, não de um mês, o que é muito mais rápido.
Entrega de dados de s3 para ClickHouse . Esse foi um dos processos mais longos. Simplesmente não funcionou fazendo o carregamento pelas ferramentas internas do ClickHouse, porque os dados no S3 estão em JSON, cada campo precisa ser extraído em seu próprio jsonpath, como fizemos no Redshift, e às vezes também precisamos usar a transformação: por exemplo, o UUID de uma mensagem de um registro padrão no formato
DD96C92F-3F4D-44C6-BCD3-E25EB26389E9
converte em bytes e insere o tipo FixedString (16).
Eu queria ter um serviço especial semelhante ao que tínhamos no Redshift como um
comando COPY . Eles não encontraram nada pronto, então eu tive que fazê-lo. Você pode escrever um artigo separado sobre como ele funciona, mas, em resumo, este é um serviço HTTP implantado em todos os hosts do ClickHouse. Você pode se referir a qualquer um deles. Os parâmetros de solicitação especificam o prefixo S3 do qual os arquivos são obtidos, a lista jsonpath para conversão de JSON em um conjunto de colunas, bem como um conjunto de conversões para cada coluna. O servidor para o qual a solicitação veio começa a varredura de arquivos no S3 e a distribuição do trabalho de análise para outros hosts. Ao mesmo tempo, é importante para nós que as linhas que não puderam ser importadas, juntamente com o erro, sejam adicionadas a uma tabela ClickHouse separada. Isso ajuda muito a investigar problemas e bugs no serviço de recebimento de eventos e nos clientes que geram esses eventos. Com a colocação do importador diretamente nos hosts do banco de dados, utilizamos esses recursos, que, em regra, estão ociosos, porque solicitações complexas não funcionam 24 horas por dia. Obviamente, se houver mais solicitações, você sempre poderá levar o serviço do importador para separar hosts.
Não houve grandes problemas com a importação de dados de fontes externas. Nesses scripts, eles apenas mudaram o destino de Redshift para ClickHouse.
Havia uma opção para conectar o MongoDB na forma de um dicionário, e não fazer cópias diárias. Infelizmente, não se encaixou, porque o dicionário deve ser colocado na memória e o tamanho da maioria das coleções no MongoDB não permite isso. Mas os dicionários também nos foram úteis: usá-los é muito conveniente para conectar bancos de dados GeoIP do MaxMind e usar em consultas. Para isso, usamos os arquivos ip_trie e CSV de layout fornecidos pelo serviço. Por exemplo, a configuração do dicionário geoip_asn_blocks_ipv4 é assim:
<dictionaries> <dictionary> <name>geoip_asn_blocks_ipv4</name> <source> <file> <path>GeoLite2-ASN-Blocks-IPv4.csv</path> <format>CSVWithNames</format> </file> <\/source> <lifetime>300</lifetime> <layout> <ip_trie /> </layout> <structure> <key> <attribute> <name>prefix</name> <type>String</type> </attribute> </key> <attribute> <name>autonomous_system_number</name> <type>UInt32</type> <null_value>0</null_value> </attribute> <attribute> <name>autonomous_system_organization</name> <type>String</type> <null_value>?</null_value> </attribute> </structure> </dictionary> </dictionaries>
Basta colocar essa configuração em
/etc/clickhouse-server/geoip_asn_blocks_ipv4_dictionary.xml
, após o qual você pode fazer consultas no dicionário para obter o nome do provedor pelo endereço IP:
SELECT dictGetString('geoip_asn_blocks_ipv4', 'autonomous_system_organization', tuple(IPv4StringToNum('192.168.1.1')));
Alterar esquema de dados . Como mencionado acima, decidimos não usar a replicação ainda, já que agora podemos nos tornar inacessíveis em caso de acidente ou trabalho planejado, e uma cópia dos dados já está no s3 e podemos transferi-los para o ClickHouse em um período de tempo razoável. Se não houver replicação, eles não expandiram o ZooKeeper e a ausência do ZooKeeper também leva à incapacidade de usar a expressão ON CLUSTER nas consultas DDL. Esse problema foi resolvido por um pequeno script python que se conecta a cada host ClickHouse (existem apenas oito deles até agora) e executa a consulta SQL especificada.
Suporte SQL incompleto no ClickHouse . O processo de transferência de solicitações da sintaxe Redshift para a sintaxe ClickHouse foi paralelo ao desenvolvimento do importador, e foi tratado principalmente por uma equipe de analistas. Curiosamente, mas o assunto não estava nem no JOIN, mas nas funções da janela. Para entender como eles podem ser feitos por meio de matrizes e funções lambda, levou vários dias. É bom que esse problema seja frequentemente abordado em relatórios sobre o ClickHouse, dos quais há um grande número, por exemplo,
events.yandex.ru/lib/talks/5420 . Nesse momento, os dados já estavam gravados de uma só vez em dois locais: no Redshift e no novo ClickHouse; portanto, quando transferimos as solicitações, comparamos os resultados. Foi problemático comparar a velocidade, uma vez que removemos uma grande coluna de propriedades, e a maioria das consultas começou a funcionar apenas com as colunas necessárias, o que, é claro, deu um aumento significativo, mas as consultas em que a coluna de propriedades não participou, funcionaram da mesma maneira ou um pouco mais rápido.
Como resultado, obtivemos o seguinte esquema:

Resultados
Resumindo, obtivemos os seguintes benefícios:
- Uma mesa em vez de 90
- Solicitações de serviço são executadas em milissegundos
- O custo caiu pela metade
- Remoção fácil de eventos duplicados
Também há desvantagens para as quais estamos prontos:
- Em caso de acidente, você terá que reparar o cluster sozinho
- As alterações de esquema agora precisam ser feitas em cada host separadamente
- A atualização para novas versões terá que fazer você mesmo
Não podemos comparar a velocidade das solicitações de frente, pois o esquema de dados mudou significativamente. Muitas consultas se tornaram mais rápidas, simplesmente porque eles lêem menos dados do disco. De uma maneira boa, essa alteração teve que ser feita novamente no Redshift, mas foi decidido combiná-la com a migração para o ClickHouse.
Toda a migração, juntamente com a preparação, levou cerca de três meses. Ela caminhou do início de julho até o final de setembro e exigiu a participação de duas pessoas. Em 27 de setembro, desligamos o Redshift e, desde então, trabalhamos apenas no ClickHouse. Acontece, já um pouco mais de dois meses. O termo é curto, mas até agora nunca houve perda de dados ou bug crítico, por causa do qual todo o cluster seria ativado. À nossa frente, aguardamos atualizações em novas versões!