Armazene com eficiência centenas de milhões de arquivos pequenos. Solução auto-hospedada

Armazene com eficiência centenas de milhões de arquivos pequenos. Solução auto-hospedada

Prezada comunidade, Este artigo se concentrará no armazenamento e recuperação eficiente de centenas de milhões de pequenos arquivos. Nesta fase, a solução final é proposta para sistemas de arquivos compatíveis com POSIX com suporte total para bloqueios, incluindo bloqueios de cluster, e aparentemente até mesmo sem muletas.

Então escrevi meu próprio servidor personalizado para essa finalidade.
No decorrer da implementação desta tarefa, conseguimos resolver o problema principal e, ao mesmo tempo, economizar espaço em disco e RAM, que nosso sistema de arquivos cluster consumia impiedosamente. Na verdade, esse número de arquivos é prejudicial para qualquer sistema de arquivos em cluster.

A ideia é esta:

Em palavras simples, pequenos arquivos são carregados através do servidor, salvos diretamente no arquivo e também lidos a partir dele, e arquivos grandes são colocados lado a lado. Esquema: 1 pasta = 1 arquivo, no total temos vários milhões de arquivos com arquivos pequenos, e não várias centenas de milhões de arquivos. E tudo isso é implementado totalmente, sem nenhum script ou colocação de arquivos em arquivos tar/zip.

Tentarei ser breve, peço desculpas antecipadamente se a postagem for longa.

Tudo começou com o fato de não conseguir encontrar no mundo um servidor adequado que pudesse salvar os dados recebidos via protocolo HTTP diretamente em arquivos, sem as desvantagens inerentes aos arquivos convencionais e ao armazenamento de objetos. E o motivo da busca foi o cluster Origin de 10 servidores que cresceu em grande escala, no qual já haviam acumulado 250,000,000 milhões de pequenos arquivos, e a tendência de crescimento não ia parar.

Para quem não gosta de ler artigos, um pouco de documentação é mais fácil:

aqui и aqui.

E docker ao mesmo tempo, agora existe uma opção apenas com nginx dentro, por precaução:

docker run -d --restart=always -e host=localhost -e root=/var/storage 
-v /var/storage:/var/storage --name wzd -p 80:80 eltaline/wzd

Seguinte:

Se houver muitos arquivos, serão necessários recursos significativos, e o pior é que alguns deles são desperdiçados. Por exemplo, ao usar um sistema de arquivos em cluster (neste caso, MooseFS), o arquivo, independentemente do seu tamanho real, sempre ocupa pelo menos 64 KB. Ou seja, para arquivos de 3, 10 ou 30 KB, são necessários 64 KB no disco. Se houver um quarto de bilhão de arquivos, perderemos de 2 a 10 terabytes. Não será possível criar novos arquivos indefinidamente, pois o MooseFS tem uma limitação: não mais que 1 bilhão com uma réplica de cada arquivo.

À medida que o número de arquivos aumenta, é necessária muita RAM para metadados. Grandes despejos frequentes de metadados também contribuem para o desgaste das unidades SSD.

servidor wZD. Colocamos as coisas em ordem nos discos.

O servidor está escrito em Go. Em primeiro lugar, precisei reduzir o número de arquivos. Como fazer isso? Devido ao arquivamento, mas neste caso sem compactação, pois meus arquivos são apenas imagens compactadas. O BoltDB veio em socorro, que ainda precisava ser eliminado por suas deficiências, isso está refletido na documentação.

No total, em vez de um quarto de bilhão de arquivos, no meu caso restaram apenas 10 milhões de arquivos Bolt. Se eu tivesse a oportunidade de alterar a estrutura atual de arquivos do diretório, seria possível reduzi-la para aproximadamente 1 milhão de arquivos.

Todos os arquivos pequenos são compactados em arquivos Bolt, que recebem automaticamente os nomes dos diretórios em que estão localizados, e todos os arquivos grandes permanecem ao lado dos arquivos; não adianta compactá-los, isso é personalizável. Os pequenos são arquivados, os grandes permanecem inalterados. O servidor funciona de forma transparente com ambos.

Arquitetura e recursos do servidor wZD.

Armazene com eficiência centenas de milhões de arquivos pequenos. Solução auto-hospedada

O servidor opera nos sistemas operacionais Linux, BSD, Solaris e OSX. Testei apenas a arquitetura AMD64 no Linux, mas deve funcionar para ARM64, PPC64, MIPS64.

Principais características:

  • Multithreading;
  • Multiservidor, proporcionando tolerância a falhas e balanceamento de carga;
  • Máxima transparência para o usuário ou desenvolvedor;
  • Métodos HTTP suportados: GET, HEAD, PUT e DELETE;
  • Controle do comportamento de leitura e escrita via headers do cliente;
  • Suporte para hosts virtuais flexíveis;
  • Suporta integridade de dados CRC ao escrever/ler;
  • Buffers semidinâmicos para consumo mínimo de memória e ajuste ideal de desempenho de rede;
  • Compactação de dados diferida;
  • Além disso, um arquivador multithread wZA é oferecido para migrar arquivos sem interromper o serviço.

Experiência real:

Venho desenvolvendo e testando o servidor e o arquivador em dados ativos há bastante tempo, agora ele está operando com sucesso em um cluster que inclui 250,000,000 milhões de pequenos arquivos (imagens) localizados em 15,000,000 milhões de diretórios em unidades SATA separadas. Um cluster de 10 servidores é um servidor Origin instalado atrás de uma rede CDN. Para atendê-lo, são usados ​​2 servidores Nginx + 2 servidores wZD.

Para aqueles que decidirem usar este servidor, seria aconselhável planejar a estrutura de diretórios, se aplicável, antes de usar. Deixe-me fazer uma reserva imediatamente: o servidor não foi projetado para amontoar tudo em um arquivo de 1 Bolt.

Teste de performance:

Quanto menor o tamanho do arquivo compactado, mais rápidas serão as operações GET e PUT executadas nele. Vamos comparar o tempo total de gravação do cliente HTTP em arquivos regulares e arquivos Bolt, bem como de leitura. É comparado o trabalho com arquivos de tamanhos de 32 KB, 256 KB, 1024 KB, 4096 KB e 32768 KB.

Ao trabalhar com arquivos Bolt, a integridade dos dados de cada arquivo é verificada (é usado CRC), antes da gravação e também após a gravação, ocorre leitura e recálculo em tempo real, o que naturalmente introduz atrasos, mas o principal é a segurança dos dados.

Realizei testes de desempenho em unidades SSD, pois os testes em unidades SATA não mostram uma diferença clara.

Gráficos baseados nos resultados dos testes:

Armazene com eficiência centenas de milhões de arquivos pequenos. Solução auto-hospedada
Armazene com eficiência centenas de milhões de arquivos pequenos. Solução auto-hospedada

Como você pode ver, para arquivos pequenos, a diferença nos tempos de leitura e gravação entre arquivos arquivados e não arquivados é pequena.

Obtemos uma imagem completamente diferente ao testar a leitura e gravação de arquivos de 32 MB:

Armazene com eficiência centenas de milhões de arquivos pequenos. Solução auto-hospedada

A diferença de tempo entre a leitura dos arquivos é de 5 a 25 ms. Com a gravação as coisas ficam piores, a diferença é de cerca de 150 ms. Mas neste caso não há necessidade de fazer upload de arquivos grandes; simplesmente não há sentido em fazê-lo; eles podem ficar separados dos arquivos.

*Tecnicamente, você pode usar este servidor para tarefas que requerem NoSQL.

Métodos básicos de trabalho com servidor wZD:

Carregando um arquivo normal:

curl -X PUT --data-binary @test.jpg http://localhost/test/test.jpg

Carregar um arquivo para o arquivo Bolt (se o parâmetro do servidor fmaxsize, que determina o tamanho máximo do arquivo que pode ser incluído no arquivo, não for excedido; se for excedido, o arquivo será carregado normalmente ao lado do arquivo):

curl -X PUT -H "Archive: 1" --data-binary @test.jpg http://localhost/test/test.jpg

Baixando um arquivo (se houver arquivos com os mesmos nomes no disco e no arquivo, então, durante o download, a prioridade é dada por padrão ao arquivo desarquivado):

curl -o test.jpg http://localhost/test/test.jpg

Baixando um arquivo do arquivo Bolt (forçado):

curl -o test.jpg -H "FromArchive: 1" http://localhost/test/test.jpg

As descrições de outros métodos estão na documentação.

Documentação wZD
Documentação wZA

O servidor atualmente suporta apenas o protocolo HTTP; ainda não funciona com HTTPS. O método POST também não é suportado (ainda não foi decidido se é necessário ou não).

Quem se aprofundar no código-fonte encontrará caramelo lá, nem todo mundo gosta, mas não vinculei o código principal às funções do framework web, exceto ao manipulador de interrupção, para que no futuro possa reescrevê-lo rapidamente para quase qualquer motor.

Fazer:

  • Desenvolvimento de replicador e distribuidor próprio + geo para possibilidade de utilização em grandes sistemas sem sistemas de arquivos cluster (Tudo para adultos)
  • Possibilidade de recuperação reversa completa de metadados em caso de perda total (se estiver usando um distribuidor)
  • Protocolo nativo para a capacidade de usar conexões de rede persistentes e drivers para diferentes linguagens de programação
  • Possibilidades avançadas de utilização do componente NoSQL
  • Compactações de diferentes tipos (gzip, zstd, snappy) para arquivos ou valores dentro de arquivos Bolt e para arquivos regulares
  • Criptografia de diferentes tipos para arquivos ou valores dentro de arquivos Bolt e para arquivos regulares
  • Conversão de vídeo atrasada no servidor, inclusive na GPU

Tenho tudo, espero que este servidor seja útil para alguém, licença BSD-3, direitos autorais duplos, pois se não houvesse empresa onde trabalho o servidor não teria sido escrito. Eu sou o único desenvolvedor. Eu ficaria grato por quaisquer bugs e solicitações de recursos que você encontrar.

Fonte: habr.com

Adicionar um comentário