GridFS — Otimizando dados multimídia

Augusto Mesquita
LazyDev
Published in
5 min readJul 17, 2019

Entenda como a especificação GridFS do MongoDB pode nos ajudar quando se trata de armazenamento de alto volume de recursos multimídia.

Se há alguns anos a prática de armazenar arquivos multimídia em bancos de dados era considerada uma má prática (e com razão, já que a maioria das bases de dados não oferecia suporte aceitável para esse tipo de trabalho), hoje essa realidade sofreu algumas mudanças.

Provavelmente você já encontrou uma situação semelhante à seguinte:

  • Você tem um grande volume de vídeos ou áudios que precisam ser disponibilizados em seu aplicativo para o usuário final;
  • Seu sistema permite que o upload desses arquivos seja realizado pelo usuário final.

Pensando em uma solução simples, você separaria uma pasta em seu servidor exclusivamente para armazenar tais recursos. Uma vez salvos nesta pasta, você deve salvar seus caminhos (URI’s) em um banco de dados SQL (como Postgres, MariaDB, entre outros).

Assim, uma vez solicitado um recurso, bastaria buscá-lo por meio de seu URI para disponibilizá-lo ao usuário final.

A forma de disponibilização pode ser atômica ou por partes (stream), esta última exigindo uma negociação do desenvolvedor, levando em consideração que os arquivos armazenados na pasta não seriam divididos em partes.

“… A forma de disponibilidade pode ser atômica ou por partes (fluxo), sendo que esta última requer uma negociação do desenvolvedor…”

Aqui estão algumas observações importantes de problemas que este método pode nos trazer:

  1. O usuário final não terá uma experiência agradável se, por exemplo, precisar baixar o áudio / vídeo completo do servidor antes de começar a consumi-lo. Em outras palavras, aqui quase teríamos a obrigação de disponibilizá-lo por stream. Isso exigiria uma série de negociações por parte do desenvolvedor e do servidor, por exemplo, dividir o recurso principal em pequenas partes ao entregar os dados solicitados ao usuário.
  2. Seu sistema de arquivos pode limitar o número de arquivos em um diretório.
  3. Se o método de stream não for adotado, o desempenho do servidor será muito afetado, principalmente se este aplicativo estiver na nuvem. Imagine que dez usuários solicitem o download de vídeos de cerca de 100 MB. Só aqui teríamos 1GB de dados sendo tratados pelo servidor. Agora, transforme esses 10 usuários em 100 … Se você não tiver um servidor poderoso e caro, o usuário final será o único a sofrer, o que é ruim para qualquer sistema.
  4. O arquivo que será lido da pasta, se for muito grande, consumirá muito desempenho do servidor, pois foi armazenado de forma atômica e será resgatado da mesma forma.
  5. Se, por acaso, o aplicativo precisar que seus dados sejam replicados em outros dbs espalhados geograficamente, o suporte para geolocalização e sincronização desses dbs não é algo intrínseco ao tipo de db que estamos usando, então teremos que fazer vários trata-se de um usuário que está mais próximo do banco de dados “DB-A”, use este ao invés de “DB-B”, que é mais distante geograficamente e tem uma maior garantia de que os dados estão devidamente sincronizados.

Todos esses problemas mencionados acima podem ser resolvidos de uma maneira muito simples usando corretamente o MongoDB junto com sua famosa especificação de armazenamento de arquivos, o famoso GridFS.

Entender como GridFS e MongoDB podem nos ajudar

Bem, para começar, vamos entender melhor a estrutura do GridFS e seu modus operandi.

GridFS divide o recurso que será armazenado automaticamente em duas coleções (fs.files e fs.chunks):

  • fs.files: Armazena informações como id, nome, tamanho, data de armazenamento e outros metadados que o desenvolvedor considera relevantes e deseja incluir, como um protocolo ou quaisquer outros dados.
  • fs.chunks: Salva o recurso.

Isso já facilita para nós quando vamos listar nossos recursos em uma interface (web, aplicativo ou qualquer outra), pois só precisamos olhar para uma coleção específica (aquela que armazena os metadados) e ela estará lá , pronto para ser consumido.

É importante observar também que o armazenamento do próprio recurso na segunda coleção acontece de forma granular, ou seja, o recurso será automaticamente dividido em blocos, conhecidos como pedaços de 255kB (valor padrão, mas podem ser modificados), exceto para o último bloco que pode ser maior do que os outros, conforme o caso.

Esses blocos de recursos são armazenados sequencialmente e, à medida que o id é indexado, a busca por um determinado recurso torna-se extremamente rápida.

Além do id sequencial, também podemos identificar exatamente qual parte do recurso que estamos vendo por meio do campo “n”

Isso já torna muito mais fácil disponibilizar recursos para o usuário por meio de um stream, pois assim que o servidor recuperar o primeiro trecho do banco de dados, a transmissão de dados será iniciada para o usuário.

Para melhorá-lo ainda mais, temos a função skip, que é suficiente para informar de qual byte queremos registrar nosso recurso e depois disso, o banco de dados retornará apenas os pedaços dos bytes restantes.

Somente com isso, conseguimos resolver os problemas dos itens 1, 2, 3 e 4 levantados no início do artigo, e o melhor: utilizando apenas as características da especificação que são feitas automaticamente pelo MongoDB e outras bibliotecas para operação do GridFS.

Se você está se perguntando como o problema 2 foi resolvido, é simples: o GridF permite quantos recursos forem necessários, é claro, obedecendo aos limites de armazenamento do servidor, que neste caso podemos dizer MongoDB (processo daemon primário do MongoDB).

O problema 5 não é tão trivial, mas usando o MongoDB a complexidade desta tarefa cai muito porque quando queremos manter nossos recursos e seus metadados automaticamente sincronizados e acomodados em vários sistemas, podemos usar o GridFS a nosso favor, pois em conjunto com o MongoDB, nos permite distribuir recursos e seus metadados automaticamente para várias instâncias do mongod por meio de uma configuração muito simples.

Mas, acalme-se! Nem tudo são flores …

Se você está planejando armazenar recursos usando GridFS e planeja fazer alterações neles, esqueça! GridFS não será uma boa opção.

Isso se deve ao fato de que, como os arquivos são armazenados separadamente (chunk), a atualização desses fragmentos é um trabalho extremamente pesado em termos de processamento.

Como alternativa, você pode armazenar várias versões de cada recurso e especificar a versão atualmente ativa em algum campo de seus metadados (_versionresource e _currentversionresource, por exemplo).

Desta forma, você precisará atualizar apenas o campo “_currentversionresource” de cada recurso da coleção “fs.files” e posteriormente remover, se necessário, as versões antigas dos arquivos através de alguma rotina de sua aplicação por exemplo.

Bem, acho que pudemos aprimorar algumas das vantagens de usar a especificação de armazenamento GridFS do MongoDB.

Em breve, trarei a você um guia passo a passo sobre como configurar / usar GridFS em conjunto com SpringData e Angular no front end.

Até então, deixe você gostar (no final do post) se gostou e compartilhe essa informação com seus amigos ou nas suas redes sociais através dos botões de compartilhamento abaixo.

Até mais!

--

--

Augusto Mesquita
LazyDev
Writer for

Um simples amante da programação e fã de Game of Thrones! Instagram:@augustomesquitasrs