Arquivos Poderosos - Guia Empolgante de Distribuição, Assincronismo e Processamento Massivo!
Tags: webdev, programming, memory, performance
Published: 2023-12-22 02:02:42

Não é todo dia que precisamos processar uma quantidade massiva de dados, mas já me deparei com casos em que precisava buscar dados anteriores de um sistema e aí os arquivos chegavam a gigabytes de dados que deveriam ser processados em uma aplicação e assim os problemas começaram a aparecer. Devo salientar que este artigo é totalmente genérico, não apresentarei nenhum trecho de código. Eu não quero lhe passar um código em uma linguagem para ler arquivos gigantes, eu quero abordar a teoria por trás desse tipo de leitura e processamento, a implementação fica por sua conta.

**Guia de tópicos:**
[Orientações para Uso Eficiente de Memória](#orientações-para-uso-eficiente-de-memória).
[Processamento Eficiente de Dados em Lotes](#processamento-eficiente-de-dados-em-lotes).
[Fluxo de Dados em Arquivos de Texto](#fluxo-de-dados-em-arquivos-de-texto).
[Fluxo de Dados em Retornos de Consultas de Banco de Dados](#fluxo-de-dados-em-retornos-de-consultas-de-banco-de-dados).
[Dividir e Processar Rápido ou Unificar e Processar Lento](#dividir-e-processar-rápido-ou-unificar-e-processar-lento).
[Considerações Finais](#considerações-finais).
[Links Indicativos](#links-indicativos).

## Orientações para Uso Eficiente de Memória

Tudo que você adiciona a uma variável é contabilizado na memória, embora alguns programadores não entendam muito bem isso, mas a visão que você tem que ter sobre isso é que o seu código tem um relacionamento sério com o computador independente de onde você esteja programando e pode ser o mais alto nível na linguagem que você trabalha e me arrisco em dizer que até desenvolvendo em _OutSystems_ você provavelmente vai ter que se preocupar com o computador onde o seu sistema vai rodar, até se você desenvolver interfaces web que será renderizado algum conteúdo no navegador do cliente você terá que se preocupar se aquela interface realmente vai carregar para o usuário independente se o computador da pessoa é uma carroça.

Então aqui vai uma lista pequena de coisas para se preocupar em relação ao computador e sua memória enquanto estiver desenvolvendo:

- Não adicione _arrays_ grandes em variáveis, o tamanho de array grande varia muito, mas vou jogar algo acima de 5000 itens, você já deve pensar em _stream_ e uma forma de dividir isso.
- Não crie variáveis de texto gigantes, você não pode de forma alguma pegar um texto inteiro de um arquivo de 1gb e colocar na sua variável, isso é um crime.
- Cuidado com chamadas demasiadas a banco de dados dentro de loops, sempre utilize _batch_, ou pense numa forma melhor de fazer o que esta fazendo.

## Processamento Eficiente de Dados em Lotes

Como mencionado no tópico anterior, ao lidar com grandes conjuntos de dados, o uso de chamadas isoladas dentro de loops pode resultar em aumento de uso de memória ou em um tempo de processamento excessivamente longo. Em ambos os casos, uma abordagem mais eficiente envolve a implementação de um sistema de processamento em lotes.

Para ilustrar esse conceito, considere o cenário em que é necessário salvar dados de um arquivo de texto no banco de dados. Suponha que o arquivo tenha 1 gigabyte e 3 milhões de linhas. Para otimizar o processo, é necessário dividir os dados em lotes e enviá-los para o banco de dados de forma agrupada. O tamanho do lote pode variar, e a escolha dependerá do desempenho observado nos testes realizados.

Assumindo que tenhamos decidido utilizar lotes de 1000, agora temos um array contendo 1000 itens. Nesse ponto, podemos realizar uma operação de _Bulk Insert_ no banco de dados, reduzindo assim o número de chamadas ao banco de 3 milhões para apenas 3 mil. Essa abordagem mais eficiente contribui para um processamento mais rápido e uma utilização mais otimizada dos recursos disponíveis.


## Fluxo de Dados em Arquivos de Texto

De maneira simplificada, um arquivo de texto no computador é armazenado como uma variável no disco, consumindo recursos de memória. Embora em discos de grande capacidade, como um terabyte, isso possa parecer insignificante, abrir um arquivo de texto extenso em qualquer programa pode causar lentidão. Isso ocorre porque a aplicação carrega normalmente o arquivo inteiro na memória RAM para exibi-lo, o que pode levar ao travamento do computador.

O mesmo princípio se aplica a qualquer aplicação em diferentes linguagens de programação, a menos que seja utilizado um conceito chamado _stream_. Uma _stream_ permite que o arquivo seja lido em partes, evitando a carga completa na memória. Isso é crucial ao lidar com arquivos extensos, pois permite processar dados caractere a caractere ou linha a linha, dependendo da implementação da linguagem. Portanto, ao trabalhar com arquivos grandes, é recomendável utilizar Streams para otimizar o desempenho e evitar problemas de travamento.

Uma abordagem altamente eficiente em situações que demandam a incorporação de dados de um arquivo em um banco de dados consiste na combinação de uma transmissão contínua _stream_ com a operação de inserção em lote _bulk insert_. Essa estratégia permite otimizar o processo, proporcionando maior eficiência e desempenho na manipulação e integração dos dados.

## Fluxo de Dados em Retornos de Consultas de Banco de Dados

Após salvar uma quantidade significativa de dados, é altamente provável que você precise recuperá-los posteriormente. Aqui surge um novo desafio: ao retornar esses dados, é recomendável ser feito de maneira paginada. Além disso, adicionar índices aos campos de filtro utilizados pelo usuário pode otimizar esse processo.

Caso necessite gerar um relatório com esses dados, uma abordagem eficiente é extrair uma _stream_ diretamente do banco de dados e salvá-la em um arquivo no formato .csv formatado. Ao finalizar esse processo, é aconselhável retornar o arquivo, preferencialmente compactado (zipado).

Se precisar utilizar outros formatos além do .csv, procure por bibliotecas que suportem operações em _stream_. No entanto, se isso não for uma exigência, é recomendável limitar o número de itens retornados pela consulta a aproximadamente 5 mil. Dessa forma, evita-se que o usuário crie relatórios com um volume excessivo de dados.

## Dividir e Processar Rápido ou Unificar e Processar Lento

Até este ponto, o artigo abordou eficazmente a resolução da maioria dos desafios associados ao processamento de arquivos volumosos. No entanto, surge um dilema crucial: o tempo. Ao optar por dividir o processamento de arquivos, seja por meio da divisão em contêineres que compartilham um volume ou pela alocação de tarefas em segundo plano usando um pool de trabalhadores, observa-se um aumento significativo na utilização dos recursos da máquina.

A conclusão que se destaca é que, quanto mais rápido se deseja processar algo, maior será o custo associado a essa operação. Isso pode se dar tanto pela necessidade de aumentar os recursos da infraestrutura para permitir que um único contêiner realize o processamento de forma mais rápida, quanto pela opção de dividir as tarefas entre vários contêineres, distribuindo a carga de processamento.

## Considerações Finais

No enfrentamento da complexidade associada ao processamento de grandes volumes de dados, é crucial reconhecer que a eficiência transcende a mera implementação de código. Envolve também uma compreensão profunda dos princípios subjacentes que regem a interação entre o software e o hardware, a aplicação de boas práticas de programação, a familiaridade com o ambiente de execução e a adoção de estratégias eficazes.

Este artigo buscou proporcionar uma visão abrangente e genérica acerca das estratégias destinadas a superar os desafios inerentes ao processamento de dados em larga escala.

## Links Indicativos
OutSystems:
https://www.mirante.net.br/low-code/

Streams:
https://pt.wikipedia.org/wiki/Stream_(computa%C3%A7%C3%A3o)#:~:text=Na%20linguagem%20Scheme%20e%20em,s%C3%A3o%20calculados%20apenas%20quando%20necess%C3%A1rio.

Bulk Insert:
https://fastercapital.com/pt/contente/Insercao-de-lote--acelerando-insercoes-de-dados-com-clausulas-em-lote.html