O melhor banco de dados para armazenar imagens pode não ser um banco de dados

Qual é o melhor banco de dados para armazenar imagens? Sempre me perguntam sobre o armazenamento de imagens, documentos, PDFs ou outros objetos binários no Couchbase. Ou, quase com a mesma frequência, o Couchbase é o melhor banco de dados para armazenar imagens?

Como arquiteto de serviços e bancos de dados, geralmente dou a mesma resposta: para esses tipos de objetos, é uma ideia medíocre armazenar arquivos em um banco de dados. Quando digo isso, estou falando com base em minha experiência pessoal de uma longa carreira trabalhando com muitos bancos de dados relacionais e NoSQL.

Sei que, do ponto de vista do desenvolvedor, é fácil e conveniente armazenar arquivos no banco de dados. Logicamente, também faz sentido fazer isso. Eu tenho dados, preciso armazená-los. Quero ter informações sobre esses dados para poder consultá-los e fornecê-los aos meus usuários. Tudo isso faz sentido para mim também.

Tenho dois argumentos para explicar por que é uma ideia medíocre armazenar esses tipos de objetos em um banco de dados (Couchbase ou outro). Também proporei uma solução para a melhor forma de usar o banco de dados de armazenamento de imagens do Couchbase a um custo eficiente e para atender melhor ao usuário final. É por isso que, no final das contas, estamos todos aqui, certo?

Despesas operacionais e desempenho

Ao armazenar permanentemente objetos grandes em um banco de dados, você estará usando suas camadas mais caras e o que deveria ser uma das camadas de melhor desempenho para objetos que geralmente são estáticos e mudam com pouca frequência na maioria dos casos de uso. Basta pensar no custo por GB de armazenamento em uma instância do EC2 no AWS em comparação com o armazenamento desse objeto no S3. Quando usei a calculadora da AWS no momento em que este artigo foi escrito, o armazenamento do S3 era pelo menos 1/5 do custo do armazenamento mais barato do EC2.

Digo pelo menos porque as configurações não são uma comparação 1:1 das possibilidades, pois elas realmente se destinam a coisas diferentes. O S3 foi criado especificamente para manter muitos objetos estáticos em uma taxa de durabilidade muito alta por um custo muito razoável, e o EC2 foi criado para ter armazenamento operacional.

Agora pense no custo físico de armazenar imagens desse objeto em um banco de dados e depois replicá-lo, fazer backup etc. por meses ou anos. Os custos e o tempo operacional para transportar esse volume de dados ficam claros. Com o tempo, esses objetos se tornam âncoras no pescoço da equipe de operações. Além disso, se você não gerenciar as coisas corretamente, poderá ter um banco de dados daqui a dois anos armazenando, fazendo backup e replicando imagens de um usuário que parou de usar seu serviço há um ano e meio, enquanto você ainda está pagando por cada KB usado várias vezes.

Toda essa sobrecarga simplesmente pela conveniência do desenvolvimento. Simplesmente não vale a pena ou não é eficiente a longo prazo. Novamente, isso não é exclusivo do Couchbase, de forma alguma.

Use cada ferramenta para o que ela faz de melhor

O Couchbase pode servir objetos diretamente do cache gerenciado na RAM com ótimo desempenho se você acessá-lo por meio do ID do objeto ou se tiver identificado o objeto desejado em uma consulta. O Couchbase também pode armazenar e servir essa imagem ou um binário maior muito rapidamente na RAM? É claro que sim, mas ele consumirá recursos caros do servidor, não apenas armazenamento, para obter esse desempenho. O Couchbase também tem uma limitação de tamanho de objeto de 20 MB. Mesmo que seus objetos não se aproximem desse tamanho, ainda assim pode ser uma má ideia armazenar permanentemente esses tipos de objetos em um banco de dados. Como mencionei anteriormente, o Amazon S3 e o HDFS são excelentes para armazenar e fornecer conteúdo estático como esse. É para isso que eles foram projetados. Eles oferecem excelente desempenho por um ótimo valor para esse tipo de carga de trabalho. É melhor usar as ferramentas certas para os trabalhos certos. Embora possamos armazenar imagens grandes no banco de dados, bem como objetos estáticos, não é para isso que eles são os melhores.

Então, como posso resolver isso para armazenar dados grandes no banco de dados? Tenho muitas coisas para armazenar!

Para este exemplo, falaremos sobre um armazenamento de imagens, já que esse é o caso de uso mais comum que ouço, mas pode ser qualquer tipo de objeto estático grande. Em um nível elevado para esse caso de uso, você deve procurar armazenar no Couchbase apenas os dados exigidos pelo aplicativo quando um usuário estiver procurando uma imagem. Ao planejar seus padrões de acesso a dados e, portanto, seu modelo de objeto de banco de dados, faça a si mesmo algumas perguntas.

  • Quais dados o aplicativo precisa apresentar ao usuário sobre cada imagem e quando no fluxo?
  • Que pesquisas serão feitas sobre a imagem e como os resultados serão apresentados? (Palavras-chave, título, criador, data de criação etc.) Se for adequado ao seu caso de uso, você pode até armazenar uma pequena miniatura da imagem no banco de dados de armazenamento para ter a opção de entrega rápida.
  • Em que momento do fluxo do aplicativo cada dado será necessário?
  • Qual será o padrão de ID de objeto para cada objeto de metadados da imagem? Para obter mais informações sobre modelagem de objetos do Couchbase e exemplos de ID de objeto, leia minhas publicações no blog aqui e aqui para obter ideias.

Agora que você tem os dados voltados para o usuário no Couchbase, pode fazer pesquisas rápidas de chaves, visualizações de redução de mapas ou consultas N1QL completas em índices secundários para obter os dados. As imagens grandes devem ser armazenadas em algo como AWS S3, HDFS, uma CDN (Content Delivery Network, rede de distribuição de conteúdo), um servidor da Web, um servidor de arquivos ou qualquer outra coisa que seja excelente para servir objetos estáticos grandes, de acordo com seu caso de uso e orçamento.

Agora vamos nos aprofundar um pouco mais e falar mais sobre como arquitetamos isso.

Exemplo de modelo de objeto

Proponho dois objetos no Couchbase para cada imagem real em seu aplicativo:

  1. Um objeto JSON que contém os metadados sobre a imagem. Ele estará em JSON para que possamos indexá-lo, consultá-lo com N1QL ou visualizações, o que for necessário para o aplicativo. Nesse objeto também estará o ponteiro para a imagem principal no outro sistema.
  2. Um objeto Key/Value que contém dados para as imagens em miniatura da imagem principal e é armazenado em um bucket separado do Couchbase. Estamos mantendo a miniatura no Couchbase para que tenhamos uma apresentação rápida dela para um usuário. Em teoria, poderíamos ter isso como um valor no documento JSON com os metadados, mas as vantagens de tê-los separados no que diz respeito à indexação e à utilização de recursos do Couchbase compensam isso, especialmente se você planeja uma alta taxa de mutação de dados.

Como cada objeto no Couchbase precisa ter um ID de objeto exclusivo (dentro de um bucket) e podemos ter 250 bytes nesse ID, vamos usar isso a nosso favor e ter um padrão de ID de objeto padronizado para recuperação fácil e rápida de objetos. Um ID de objeto padronizado nos ajudará a recuperar com facilidade uma imagem e seu conteúdo relacionado rapidamente do Couchbase Data Service ou ao fazer consultas usando o N1QL.

O padrão de ID de objeto para cada documento será o seguinte:

  • objeto de metadados: metadados:: em que o identificador exclusivo é atribuído a essa imagem pelo aplicativo. Como vamos encontrar imagens por meio de consultas com o N1QL, não vou usar um ID de objeto mais descritivo.
  • Objeto de miniatura: miniatura:: em que o nome é aquele que usamos para o objeto de metadados. Dessa forma, estabelecemos uma relação informal entre os objetos. Sabemos que cada metadados tem um objeto miniatura objeto. Portanto, se precisarmos obter os dois, podemos fazê-lo. Quando soubermos o UUID, poderemos obter a miniatura rapidamente ou vice-versa.

Para os próprios objetos:

  • O objeto de metadados será um documento JSON e poderá ser parecido com o seguinte:
    {

    "title" : "Cute Kitty and Doggy",

    "file-location" : "https://s3.amazonaws.com/kittypics/cutekittyanddoggy.jpg",

    "thumbnail1" : "thumbnail::

    “,

    "dimensions-px" : "50×50"

    }

    A parte da miniatura onde está escrito

    seria substituído pelo ID desse objeto, é claro. Dessa forma, quando obtivermos o objeto de metadados, teremos o ID do objeto de miniatura e poderemos pegá-lo rapidamente. Esse é um daqueles momentos em que provavelmente é melhor fazer várias chamadas para o Couchbase do que em outro banco de dados, onde seria melhor fazer isso em uma única chamada.

  • O objeto de miniatura será simplesmente uma chave/valor, sendo o valor binário.

Configurações específicas do Couchbase

Baldes

Proponho dois buckets do Couchbase. Um para os documentos JSON que contêm os metadados sobre cada imagem e outro para a miniatura. Os dois motivos específicos para isso são:

  1. Os buckets separados permitem o ajuste do cache gerenciado de acordo com a necessidade do objeto. Por exemplo, talvez eu queira que os metadados de cada imagem estejam sempre disponíveis o mais rápido possível. Dimensiono a cota de RAM do compartimento "metadados" para ter todos esses objetos no cache gerenciado para obter o melhor desempenho. Quando se trata do armazenamento de dados de valor-chave para miniaturas, talvez queiramos economizar um pouco no tamanho de nossas instâncias e não manter tantas delas no cache, porque se elas aparecerem alguns segundos depois, não há problema. Poderíamos dimensionar a cota de RAM para o bucket de metadados para 300 GB em todo o cluster, mas as miniaturas para 50 GB em todo o cluster, embora as miniaturas possam ser o maior conjunto de dados em disco.
  2. Nunca precisaremos indexar ou consultar os objetos de miniatura. Sempre podemos pegá-los pelo ID do objeto que obtivemos do documento JSON de metadados ou pelo aplicativo que o está construindo. Para aprofundar um pouco mais o motivo pelo qual queremos esses objetos em dois buckets separados, quando você faz a indexação no Couchbase, cada objeto em um bucket é interrogado em algum momento para ver se deve ser incluído em um índice. Isso é feito pelo View Indexer, se você estiver usando Views, ou pelo Projector, se estiver usando GSI (Global Secondary Indexing). Se tivermos esses dois tipos de dados em compartimentos separados, o indexador e o projetor, precisaremos consultar os documentos JSON e nunca precisaremos nos preocupar com os objetos de miniatura e desperdiçar ciclos ou recursos, já que os índices são específicos do compartimento. Outro bônus é que, se você estiver usando as visualizações do Couchbase que são armazenadas junto com os dados, isso deverá manter os tempos de rebalanceamento do cluster baixos, pois novamente o View Indexer não precisará interrogar as miniaturas à medida que os dados se movem. Em geral, isso significa que você precisa de menos recursos de servidor, portanto, é mais econômico.

Para fins deste exemplo, vamos chamar os dois compartimentos de algo enigmático como "metadados" e "miniaturas".

Value Evict (o padrão) ou Fully Evict

É mais do que provável que você queira evitar o uso do recurso de despejo completo do Couchbase para esse caso de uso específico. É um ótimo recurso, mas parte do motivo para armazenar esses objetos de metadados de imagem no Couchbase é a funcionalidade, mas também o desempenho que você obtém do cache gerenciado. É mais do que provável que seu caso de uso exija a verificação da existência de um objeto em algum ponto do fluxo do aplicativo. Se esse for o caso, usar o despejo completo será ruim, pois você terá de ir ao disco para verificar isso. Se você usar o "despejo de valor" padrão, poderá saber rapidamente se o objeto existe, pois os dados do Couchbase sobre cada objeto estarão no cache gerenciado o tempo todo. Portanto, use esse recurso com sabedoria e só ative o despejo total se souber exatamente o que ele fará com seu aplicativo e por quê.

Uma exceção à regra

Como sempre, há exceções que vão contra as regras. Conheço um cliente do Couchbase que coloca objetos binários (arquivos de áudio, para ser mais específico) no Couchbase Server com um sucesso incrível. No entanto, eles fazem isso por um motivo muito específico que usa o Couchbase a seu favor. Eles inserem gravações de áudio no Couchbase, mas o segredo é que o aplicativo divide os arquivos de áudio em partes menores e transmite cada uma delas para o Couchbase à medida que chegam, juntamente com um documento de metadados para essa gravação. O interessante é que eles não permanentemente armazenar o arquivo de áudio no banco de dados pelos motivos que já mencionei neste artigo. Após alguns minutos, se o arquivo de áudio não tiver sido acessado, um processo em segundo plano reconstrói cada arquivo e o move para o Amazon S3 para armazenamento de longo prazo. Em seguida, eles atualizam o documento JSON de metadados do arquivo de áudio com um ponteiro para o arquivo no S3. Ingestão muito rápida e de alta velocidade com o Couchbase e armazenamento de objetos estáticos de longo prazo com o S3. Esse é um ótimo exemplo do uso das melhores ferramentas para aquilo em que elas são melhores.

Resumo

Não faça as perguntas erradas. Qual é o melhor banco de dados para imagens? Qual é o melhor banco de dados para armazenar arquivos? Colocar permanentemente objetos maiores em um banco de dados é uma ideia medíocre, na melhor das hipóteses, independentemente da plataforma de banco de dados que você usa. Mesmo que haja um sistema de arquivos simulado especial no banco de dados que dividirá seus arquivos binários grandes em arquivos menores para armazená-los no banco de dados e remontá-los automaticamente para você. Os mesmos conceitos se aplicam. Você está trocando a facilidade de desenvolvimento por uma vida cara e operacionalmente mais difícil no futuro. Isso o assombrará mais tarde.

Para obter a melhor solução, use cada ferramenta para aquilo em que ela é melhor. Armazene no Couchbase um documento JSON de metadados para cada objeto, talvez, no máximo, uma pequena imagem em miniatura. Nesse documento estão os dados de que você precisa sobre esse objeto em seu aplicativo rapidamente, mas também um ponteiro para um armazenamento de objetos criado para fins específicos, como o S3, um sistema de arquivos ou o HDFS. Você terá o melhor de todos os mundos. Desempenho, facilidade de operações e economia sem muito trabalho extra.

Não concorda? Tem outra exceção à regra? Adicione-a nos comentários e vamos conversar.

Compartilhe este artigo
Receba atualizações do blog do Couchbase em sua caixa de entrada
Esse campo é obrigatório.

Autor

Postado por Kirk Kirkconnell, engenheiro de soluções sênior, Couchbase

Kirk Kirkconnell foi engenheiro de soluções sênior da Couchbase, trabalhando com clientes em várias capacidades para ajudá-los a arquitetar, implantar e gerenciar o Couchbase. Sua experiência é em operações, hospedagem e suporte de infraestruturas de aplicativos e bancos de dados em larga escala.

Deixe um comentário

Pronto para começar a usar o Couchbase Capella?

Iniciar a construção

Confira nosso portal do desenvolvedor para explorar o NoSQL, procurar recursos e começar a usar os tutoriais.

Use o Capella gratuitamente

Comece a trabalhar com o Couchbase em apenas alguns cliques. O Capella DBaaS é a maneira mais fácil e rápida de começar.

Entre em contato

Deseja saber mais sobre as ofertas do Couchbase? Deixe-nos ajudar.