Jun 09 2007

Campo totalizador ou cálculo dinâmico: qual técnica você usa?

Autor: Marcos Dell Antonio - Categorias: Cotidiano

Recentemente dei de cara com um problema de performance em um dos sistemas que trabalho: o cálculo dinâmico do saldo de algumas contas a pagar e receber ficou extremamente lento devido a quantidade de registros existentes.

Duas semanas depois o mesmo problema aconteceu numa situação semelhante: o cálculo do saldo dos produtos demorava cerca de 40 minutos!

Até então eu sempre utilizei o cálculo dinâmico para obter o saldo ou total de alguma conta, produto, etc. Em outras palavras, ao exibir um relatório com o saldo de todas as contas a pagar e receber, por exemplo, eu retornava todas as contas e calculava uma a uma o saldo atual.

Esta abordagem foi boa enquanto o número de registros era pequeno. Depois que ultrapassou 50.000 (que não é algo tão monstruoso, mas já foi o suficiente para prejudicar a performance) a coisa complicou. O tempo de resposta ultrapassou a barreira dos minutos e o telefone não parava de tocar.

Qual foi a solução que encontrei? Utilizar um campo totalizador.

Tomando como exemplo as contas a pagar, esta técnica implica na criação de um campo chamado SALDO na mesma tabela que registra as contas. Este, por sua vez, deve ser atualizado sempre que uma alteração na conta é realizada (baixa, cancelamento de baixa, etc).

Depois de empregar esta técnica, relatórios que antes demoravam cerca de 40 minutos hoje levam no máximo 10 ou 20 segundos para serem gerados. No entanto, ela possui alguns pontos negativos:

  • Em sistemas legados modelados incorretamente, onde a inclusão e alteração de contas acontecem em vários locais utilizando diferentes métodos e formas, a atualização do saldo a cada movimentação fica comprometida, pois o desenvolvedor precisa lembrar de todos os locais onde deverá alterar para que o saldo seja atualizado de forma correta. Caso esqueça de algum, os problemas começam a surgir;
  • Novamente em sistemas legados, com uma base já populada, é preciso atualizar todas as contas já movimentadas para que o campo SALDO tenha o valor correto (isso pode levar horas e horas);
  • Sempre que uma movimentação é feita o saldo deve ser atualizado e, portanto, novas rotinas são executadas. Isto acrescenta um tempo considerável de processamento à rotina que salva os registros.

Os três itens acima demonstram o preço (ou parte dele) que se paga para incrementar a performance em uma aplicação que tende a crescer exponencialmente.

Antes de adotar esta técnica do campo totalizador, resolvi conversar com um colega que desenvolve utilizando o banco de dados Oracle. Apesar de todos os recursos existentes nesta plataforma, ele também me disse que todos os cálculos deste tipo eram previamente feitos na movimentação e na hora de consulta só eram exibidos.

Se alguém tiver alguma opinião sobre o assunto, gostaria muito de sabê-la. :)

Até +.

Adicione ao del.icio.us del.icio.us | Adicione ao Rec6 Rec6

14 comentários para “Campo totalizador ou cálculo dinâmico: qual técnica você usa?”

  1. Caio Proieteem 09 Jun 2007 10:55 pm

    Marcão, são 2:55 da madrugada, então posso não ter lido seu post direito e consequentemente não entender a complexidade do seu problema, mas ao que me parece, este é um problema comum e que tem uma solução simples.

    Em geral, você tem uma tabela de Contas e outra de Movimentos das Contas.

    Quando digo Contas, não estou me referindo a cada conta à pagar ou à receber. Estou falando da conta da empresa.

    Então, o mais comum, é criar o campo “Saldo” na tabela de Contas, e não em cada linha da tabela de movimentos. Afinal, é o saldo da Conta, certo?

    A cada alteração na tabela de movimentos, o campo Saldo da tabela de Contas é atualizado e pronto. Simples assim.

    Obviamente, deve-se utilizar algum mecanismo de “Lock” para serializar as alterações nesse campo e garantir que se vários usuários modificarem a tabela de movimentos, todas as alterações serão refletidas no campo saldo para aquela determinada conta, além de envolver todas as instruções SQL em uma transação para garantir que não haverão inconsistências.

    Se o teu sistema tiver muitos usuários conectados ao mesmo tempo e a serialização deixar o processo muito lento, então torne a operação assíncrona e utilize um sistema de fila de mensagens, como o MSMQ, por exemplo.

    Respondi tua dúvida ou compliquei mais?

    Abraços,
    Caio Proiete

  2. Marcos Dell Antonioem 10 Jun 2007 6:18 pm

    Opa!

    Caio, a solução que uso no dia-a-dia é com o campo saldo na tabela de contas (tomando como base o seu exemplo).

    Porém, isto implica em uma atualização deste campo a cada movimento realizado, e se você não tiver um modelo de classes bem definido, onde a inserção e alteração de movimentos sejam feitas em um único lugar, o risco do campo saldo não estar de acordo com os movimentos é grande.

    Se puder me ajudar com as seguintes perguntas fico grato:

    - Você costuma usar esta técnica também ou é adepto do cálculo dinâmico?
    - Como você criaria a rotina de atualização de saldos? Usaria uma Stored Procedure (ou algo do tipo) ou uma rotina em C#, por exemplo?

    Até.

  3. Cassio Eskelsenem 11 Jun 2007 9:07 am

    Bom, como não tenho medo de ser crucificado, o sistema onde o Marcos encontrou esse problema foi inicialmente idealizado por mim.

    Após alguns anos de trabalho com sistemas onde haviam saldos envolvidos, cheguei a conclusao de que a utilização de campos do tipo “saldo” é uma solução pobre, sujeita a falhas e consequentemente imprecisa.
    Para nós desenvolvedores é muito mais fácil a utilização do campo “saldo”, no entanto, em sistemas onde saldos são vitais, como por exemplo um sistema contábil, precisamos levar em conta primeiramente a precisão do sistema. Essa precisão só encontrei computando-se os saldos dinamicamente.

    A utilização do campo saldo só será precisa SE e SOMENTE SE:

    a) houverem mecanismos de lock 100% confiáveis
    b) houver apenas um ponto do sistema responsável pela atualização dos saldos
    c) Se houver mais de um sistema que necessite modificar o saldo, deve-se disponibilizar mecanismos de atualização (exemplo, webservices)
    d) Nunca, em hipótese alguma, deve-se abrir a base para que terceiros possam modificar os dados

    A questão da baixa performance pode ser resolvida com a utilização de stored procedures e/ou views.

    Por outro lado, como não sou inflexível, gostaria de saber qual é solução adotada pelos bancos, por exemplo. Acredito que eles, mais do que ninguém, devem ter a melhor solução em termos de performance X confiabilidade.

    abraços

    cassio

  4. Caio Proieteem 11 Jun 2007 9:42 am

    Fala Marcão!
    Pelo teu texto (somado ao alto indice de sono que estava), tinha entendido que você estava mantendo o campo “Saldo” na tabela de movimentos, e pensei que esse era o principal dos teus problemas. Então, respondendo suas perguntas:

    a) Não sou adepto ao cálculo dinâmico em tabelas que tem tendência para ter muitos registros justamente pelo problema de performance.

    b) Se eu tivesse um modelo de classes bem definido, implementaria a atualização do saldo na camada de dados, utilizando stored procedures, e envolveria tudo em uma transação para garantir que todas as instruções são executadas.

    Já se meu modelo classes estivesse bagunçado, utilizaria, então, Triggers na tabela de movimentos, para atualizar o saldo a cada alteração da tabela movimentos. Com certeza penalizará a performance um pouquinho, mas não tanto como o cálculo dinâmico, e as classes podem continuar como estão.

    Abraços,
    Caio Proiete

  5. Marcos Dell Antonioem 11 Jun 2007 1:18 pm

    Cássio,

    Eu também gostaria de saber como os bancos fazem para resolver este problema. Todos os exemplos que meu professor de sistemas distribuídos na Universidade utilizou, eram com o campo saldo e utilizando mecanismos de Lock.

    Caio e Cássio,

    O sistema que desenvolvo ainda utiliza o banco de dados MySQL 3.x. Logo, soluções como Views, Triggers e SPs estão fora de cogitação.

    Porém, já que vocês lembraram delas, vou deixar minha opinião a respeito.

    Acho que são soluções muito específicas para determinados bancos. Normalmente a criação de SPs difere de um pra outro e a utilização de Triggers também. Logo, para quem utiliza um framework no estilo NHibernate ou deseja tornar o sistema independente de bancos, acaba tendo que abrir mão destas funcionalidades.

    Portanto eu acho que a solução mais viável quando o sistema tende a ser independente de bancos é com o campo saldo e as atualizações feitas via código (C#, Delphi, etc).

    Ótima discussão.

    Até!

  6. Caio Proieteem 12 Jun 2007 7:49 am

    Realmente, ótima discussão.

    Não sei como todos os bancos fazem, mas trabalhei no Itaú e sei como eles fazem: Campo “Saldo”. Para tentar diminuir o risco de erros, existem serviços que recalculam o saldo das contas, durante a madrugada, para não atrapalhar a performance dos sistemas durante o dia.

    No caso desse sistema do Marco, essa funcionalidade pode ser facilmente implementada em um Windows Service desenvolvido em .NET, por exemplo.

    Todos os ítens enumerados pelo Cassio são regras básicas para qualquer sistema confiável e que seja utilizado por mais de uma pessoa… Independente de ter campos “Saldo” ou não.

    Todo banco relacional tem obrigatóriamente um sistema de lock 100% confiável, e nunca devemos abrir nossa base de dados para terceiros… Nem mesmo o nosso “Cadastrinho de Clientes”. Por isso, não acho que essa seja a preocupação.

    A diferença entre executar uma Stored Procedure com um SUM de 50.000 registros e executar uma instrução SQL diretamente com um SUM de 50.000 registros é milésimos de segundo. Stored Procedure não faz milagre… O problema estão nos 50.000 registros :)

    Finalmente, se o sistema possui um modelo de classes bem definido, não há qualquer problema nesse tipo de implementação.

    A verdade é que por melhor que seja o banco de dados e por melhor que seja a configuração do servidor, sempre existirá um número de registros X em que a performance será prejudicada. Por isso, não adianta fugir do campo “Saldo” :).

    Abraços,
    Caio Proiete

  7. Cassio Eskelsenem 12 Jun 2007 5:11 pm

    Caio,

    Talvez você viva em alguma realidade paralela, mas todos sabemos que na prática a teoria é outra. Por mais básicas que sejam essas regras, nem sempre elas são possíveis de serem implementadas, seja por questões técnicas, seja porque o chefe mandou.

    Cassio

  8. Marcos Dell Antonioem 12 Jun 2007 7:35 pm

    E o chefe as vezes manda mesmo você fazer algo que não deve. Seja por falta de conhecimento ou de tempo, isso vai e vem acontece.

    Sintetizando o que eu penso sobre este assunto:

    A solução, mais uma vez, não é exata tal como tenta ser é a ciência que estuda ela (computação).

    A técnica ideal, no meu ponto de vista, depende da situação. Se você trabalha a vida inteira com SQL Server, sabe que o sistema em questão vai administrar umas lojinhas fundo de quintal e que não terá problemas de desempenho pois o cliente só quer saber do saldo das contas no fim do mês, então acho que uma solução em runtime (cálculo dinâmico) cai bem.

    No entanto, se passa pela sua cabeça que a base de dados pode crescer exponencialmente ou você não conhece a fundo o banco de dados que está usando, o campo saldo é a melhor opção.

    Mais uma vez, esta é só a minha opinião.

    É isso ae. Até +.

  9. Caio Proieteem 13 Jun 2007 10:40 am

    Cassião e Marcão,

    Não vou alogar para não desviar muito do tópico, mas quando vocês falaram de modelo de classes, Camadas, NHibernate e outros nomes fancy, pensei que estávamos discutindo o desenvolvimento de sistemas profissionais, mas ficou claro que estamos falando de automatizar o “bar do zé”, e como não tenho mesmo experiência nessa área, fico por aqui.

    Se você é consultor, e é chamado para implementar uma solução, o “chefe” é você, e quem vai aplicar as metodologias é você. O cliente está “desejoso” de ouvir a opinião do especialista no assunto, e vai seguir à risca. Esse é o mundo onde vivo.

    Agora, se você é programador de uma software house pequenina, bem… Ai sim, o mundo é completamente outro.

    Talvez o Cassião tenha razão. São mesmo realidades paralelas.

    Abraços,
    Caio Proiete

  10. Cassioem 14 Jun 2007 8:36 am

    Eu tinha prometido para o marcos que não alongaria essa discussão, mas como não curto prepotência, resolvi continuar.

    Caio, em nenhum momento “ficou claro que estamos falando do bar do zé” ou “de software-houses pequeninas”. Já vi putaria em código de pequenas empresas e putaria em código de grandes empresas. A empresa onde o Marcos está agora, e que já foi minha, é pequena, mas em termos de código, ao menos na minha época, era uma das mais organizadas. Éramos um dos poucos(talvez os únicos) de Blumenau a ter, por exemplo, versionamento de código, isso a 7/8 anos atrás já.
    Não é o porte da empresa que vai ditar a qualidade do seu código.

    Se você vive em um mundo onde não há pressão por prazos e pode usar o estado-da-arte, ótimo para você, mas seria interessante você descer do seu pedestal e reconhecer que a imensa maioria dos projetos não é assim, infelizmente.
    Eu até acho interessante que você fala que é “consultor que é chamado” que dita a metodologia. Pensei que em grandes empresas, que é o mundo onde você diz viver, haviam equipes de desenvolvimento, não um salvador da pátria que fizesse tudo.

    ————————

    Eu acho bizarra essa mania que existe em nossa área de se colocar em um patamar de superioridade: “eu não trabalho com sistemas para o ‘bar do zé’”, ou ‘lojinhas’, ou outros termos pejorativos. Só falta criarmos um sistema de castas dentro de nossa área, onde teríamos Brahmanes que atuariam em sistemas de grande porte como para um Banco do Brasil da vida, e dalits que teriam por função automatizar o “bar do zé” e uma casta não pudesse se misturar com a outra.

    ————————-

    Saindo da sessão quebra-pau, me lembrei de um detalhe. Quando foi modelado o sistema que o Marcos está trabalhando, a questão da performance foi discutida. Uma solução que apresentei na época, e que não sei se está sendo levada em conta, é a criação de _snapshots_ de saldos através de processos que rodariam em background. Assim teríamos um meio termo entre as duas possibilidades.

  11. Caio Proieteem 14 Jun 2007 10:50 am

    Cassião,

    Sejamos realistas. Quem dita a qualidade do código é o tamanho do cliente. Código de qualidade custa caro, e o “bar do zé” não pode pagar por isso.

    Pelo seu texto, ficou claro que você não conhece, mas grandes empresas, possuem além da equipe de desenvolvimento, um quadro chamado “Arquiteto de Soluções”, que é o consultor que mencionei anteriormente.

    Não tem salvador da pátria nem pedestal. Tem um especialista, responsável em garantir o sucesso do projeto.

    Voltando ao seu primeiro comentário, que originou a continuação dessa discussão, você escreveu:

    a) houverem mecanismos de lock 100% confiáveis

    b) houver apenas um ponto do sistema responsável pela atualização dos saldos

    c) Se houver mais de um sistema que necessite modificar o saldo, deve-se disponibilizar mecanismos de atualização (exemplo, webservices)

    d) Nunca, em hipótese alguma, deve-se abrir a base para que terceiros possam modificar os dados

    Me desculpe, mas novamente repito que estes são itens básicos em qualquer sistema profissional.

    Essa conversa do “o chefe manda fazer diferente”. Bem… Se o seu chefe viola um dos itens básicos acima, você está muito mal de chefe, meu caro.

    O desenvolvimento é profissional ou é “bar do zé”, não existe meio-termo, uma vez que:

    - Todos os bancos de dados “decentes” tem mecanismos de lock confiáveis

    - Um sistema profissional tem um modelo de classes bem definido, e protegerá o acesso indevido à um determinado trecho de código.

    - Nunca, em hipótese alguma, deve-se abrir a base de dados de qualquer sistema.

    Será que fui claro dessa vez?

    Abraços,
    Caio Proiete

    PS: Marcão, se estivermos poluindo demais o seu blog, “dá bronca em nóis, que nóis pára!”.

  12. Marcos Dell Antonioem 14 Jun 2007 9:31 pm

    Caio,

    Deixa disso rapaz! Desde quando uma discussão polui um blog? Como diria o outro: tocalo pau. Enquanto vocês não falarem de mães, irmãs ou parentes, tá valendo. ;)))

    Sobre o assunto, vamos ao que está acontecendo comigo.

    O que estou fazendo…

    Há duas semanas estou removendo todos os cálculos dinâmicos do sistema e adotando os campos de saldo. Desde a tabela de plano de contas até o cadastro de produtos. Tudo que é tipo de cálculo dinâmico está sendo substituído pelo campo saldo.

    Nós não usamos um banco muito bom (MySQL 3.x), então como todas as soluções a nível de BD falharam, o negócio foi partir pra otimização via modelo.

    Como gostaria que fosse…

    Confesso a vocês (Cássio e Caio) que gostaria muito de ter um sistema utópico como todo javeiro sonha que tem. Aquele modelo de classes altamente atualizado, com baixo acoplamento, componentizado como manda o figurino e, o melhor de tudo, com um desempenho excelente.

    Como é…

    Porém, sou o único programador experiente trabalhando em cima de um sistema com mais ou menos 8 módulos (um mini ERP) e uma boa quantidade de tabelas e cadastros. Sem contar o número infinito de programadores que já botaram as mãos nele.

    Já imaginam pelo que estou passando? Quatro ou cinco lugares inserindo registros na tabela de estoque e/ou financeiro, mais alguns locais removendo e por aí vai. Eu diria que nem 50% do sistema segue um modelo de classes.

    Tento fazer o melhor, mesmo. Já criei diversos métodos no estilo AtualizaSaldo onde todo o processo acontece dentro dele. Mas é complicado, e muito.

    Conclusão…

    No meu caso a regra é clara: faça do jeito que der, mas faça.

    Até.

  13. [...] mesmo, aqui no meu blog eu postei um comentário que diz o seguinte: (…) gostaria muito de ter um sistema utópico como todo javeiro sonha que [...]

  14. Christianem 02 Apr 2008 5:36 pm

    Caros,

    Acompanhei a discussão com interesse, pois passo pelo mesmo “problema”. Mas me surgiu uma idéia.

    Talvez um conjunto das propostas — saldo calculado dinamicamente e uso de campo saldo — possa ser usado.

    Como poderia ser isso: a cada virada de mês, o saldo é calculado e colocado em um campo “saldo”. Assim, para saber o saldo do dia 16, calcula-se dinamicamente somente as entradas entre os dias 1º a 15, e então soma-se o “saldo do mês anterior”.

    Evidentemente, o uso desta solução dependerá muito dos requisitos do negócio…

Faça um comentário