Performance e Melhores Práticas

Pessoal, tenho umas dúvidas sobre melhores práticas e performance:

  1. Qual a melhor prática para quando quero inserir um registro, mas verificar se já existe algum registro com essa chave primária, atualizar ele (algo como um REPLACE INTO do Mysql, ou MERGE INTO do Oracle). Normalmente, eu faço o update e verifico quantas linhas foram atualizadas, se foi atualizada algum valor maior que 0(se for, sempre será 1, hehehe) eu não insiro o registro. (por enquanto não estou usando Hibernate, somente PreparedStatement)

Mas faço isso dependendo da minha estrutura de índices, por exemplo, se eu só tiver como índice a chave primária, normalmente eu coloco o INSERT primeiro e dentro do catch eu coloco o update (somente se for erro de chave primária). Notei que em alguns casos isso deixa um pouco mais rápido (visto que ele vai direto na PK, e como nem todos os SGBDs fazem UPDATE através da PK quando usamos um WHERE que restringe ela, dessa forma eliminamos até a consulta a tabela de estatística que vai definir se usa índices ou não).

Como a melhor prática indica que procedamos? (nem sei se existe esse “procedamos” em português, heheheh)

  1. Em uma aplicação WEB, qual a melhor forma de fazer cache de banco de dados. Pensei em colocar um objeto na JVM que sempre está conectado ao banco, que sempre que uma determinada query for executada, ele verifique se a última execução dessa query faz menos que um minuto. Se fizer menos, retorna o ResultSet cacheado(inventei uma palavra nova, heheh, mas acho que dá para entender), se não, atualiza o ResultSet com uma consulta ao banco. No caso, uso um HashMap, em que a chave é a própria query (no caso de queries que não usam parâmetros, as queries que usam parâmetros executo a toda requisição). Existe alguma prática melhor que essa?

  2. Como uso o framework JSF, com o view handler do Facelets, para gerar cache de conteúdos que não mudam muito eu gravo dinamicamente um XHTML, e esse XHTML é “incluído” ou acessado diretamente como JSF pelo cliente. Essa é a melhor prática?

  3. Outra coisa é sobre o POOL de conexões, normalmente vocês limitam a uma determinada quantidade de conexões simultâneas, ou usam somente uma conexão e, talvez, uma conexão por usuário? Costumam armazenar em outra thread ou na JVM? (eu costumo armazenar na JVM, mas vi que muitos programadores .NET criam uma thread só para controlar as conexões) Qual seria a vantagem de cada uma delas?

Bom, se tiverem qualquer sugestão ou comentário, resposta concreta, ou mesmo alguma dúvida a respeito, gostaria que postassem, para que possamos compartilhar conhecimento!

Nossa, quantas dúvias ao mesmo tempo… mas vamos por partes :wink:

Geralmente o recomendado é vc verificar isto, em vez de trabalhar no “erro”, colocando um update dentro de um catch… agora, uma pergunta, se vc tiver um objeto que tem a primary key setada… não quer dizer que quando vc for ao banco, vc já pode considerar que será para um update?

Não é recomentado fazer um cache do banco na “mão”, tem frameworks para isto.
porque não/criar um cache: vc teria que implementar todo o controle de transação na mão, e garantir a integridade do cache, só em falar em “implementar uma cache” já teria um certo problema, pq é recomentado vc usar caches já prontas, que tenham todo uma infra já feita…

Agora outra pergunta, isto é uma aplicação de alta performace? pois geralmente um select numa linha específica do banco não costuma “demorar”, ou ser “pesado”…

Em relação ao uso de frameworks para isto… dê uma olhada no JPA, ele é vem completo.
http://download.oracle.com/docs/cd/E17477_01/javaee/5/tutorial/doc/bnbpy.html

Seguinte, esta estoria de pools, não é mais necessário “implementar” um do zero, todos os containers, applications servers que conheco tem um embutido… desta forma tme um cara que gerencia para vc o estas paradas… e sim, geralmente é legal vc configurar o pool para que ele tenha mais de uma conexão aberta… desta forma seu sistema não fica threadsafe em relação ao banco.

Espero ter ajudado.

Hum… não é bem assim, porque você pode ainda assim ter de capturar uma exceção porque há um certo tempo que transcorre entre você verificar se existe o registro e adicionar ou atualizar o registro. Pode ocorrer de o registro não ser achado, então você tentar inserir no banco mas nesse meio tempo outra aplicação (ou outra thread de sua aplicação) conseguir inserir, a menos que você use alguma coisa específica do banco como um “SELECT FOR UPDATE” ou coisa parecida, que trava a tabela nesse meio tempo.

Eu recomendaria tentar atualizar primeiro e inserir depois, se houver algum problema.

[quote=entanglement][quote=BrunoCarlo]
Geralmente o recomendado é vc verificar isto, em vez de trabalhar no “erro”, colocando um update dentro de um catch… agora, uma pergunta, se vc tiver um objeto que tem a primary key setada… não quer dizer que quando vc for ao banco, vc já pode considerar que será para um update?
[/quote]

Hum… não é bem assim, porque você pode ainda assim ter de capturar uma exceção porque há um certo tempo que transcorre entre você verificar se existe o registro e adicionar ou atualizar o registro. Pode ocorrer de o registro não ser achado, então você tentar inserir no banco mas nesse meio tempo outra aplicação (ou outra thread de sua aplicação) conseguir inserir, a menos que você use alguma coisa específica do banco como um “SELECT FOR UPDATE” ou coisa parecida, que trava a tabela nesse meio tempo.

Eu recomendaria tentar atualizar primeiro e inserir depois, se houver algum problema. [/quote]

Isto é o controle de transação, também citado. Mas trabalhar em cima do erro, SEMPRE, não é legal não, pq se houver uma preocupação com a performace do banco, que tem um custo grande em verificar a integridade toda vez que é feito uma transação, no caso o update, esta prática irá derrubar qq tentativa de melhora…

E além disto, fazer um update, se der erro insere… também vai cair no seu exemplo de coisa que dará errado, ex.:

Thread 1, faz update, dá erro, então ela vai inserir… mas no meio tempo
Thread 2, faz update, deu erro… não existe… então vai inserir, no meio tempo, novamente
Thread 1 insere… blz
Thread 2 insere… erro…

O melhor jeito de controler isto, é sem dúvida o controle de transação… não estou dizendo que resolverá, pois não existe mundo perfeito… mas mais uma vez, trabalhar em cima de erros é ruim para todos… banco e aplicação.

Isso é verdade. Se for usado o controle de transação, basta tentar checar se o registro existe (select) para travar a tabela por tempo suficiente para você fazer um insert, ou então travar a linha para fazer um update (o travamento costuma ser automático nesses casos). Nesse caso, não é necessário ficar tratando esses erros. Depois de fazer a inserção ou atualização, basta encerrar a transação.

Primeiro de tudo, obrigado pelas respostas. Vou tentar trabalhar um pouco sobre suas respostas mesmo para tentar explicar meu ponto de vista.

Compreendo que pela clareza do código, pelo que aprendemos “nos livros”, etc… Mas me refiro em questão de performance.

Estou estudando mais é “formas de aumentar a performance”, pois é isso que vai fazer a diferença para o cliente.

[quote]agora, uma pergunta, se vc tiver um objeto que tem a primary key setada… não quer dizer que quando vc for ao banco, vc já pode considerar que será para um update? [/quote]Vou te explicar como costumo escrever um código que use isso (depende muito do contexto, normalmente busco criar um objeto já persistindo em disco):

Eu tenho uma aplicação web em que eu cadastro um usuário, por exemplo, mas é possível, durante o cadastro, o usuário postar uma foto. Nesse caso, eu crio um registro temporário, com uma descrição temporária, no momento que ele clica no “novo usuário”, para que quando upar a foto, tenha um registro para a FK fazer referência.

Quando ele grava, eu altero os dados necessários com um update, até aí tudo bem. Mas acontece que um usuário pode desistir do cadastro no meio do processo (e nesse caso, nem sempre sabemos que ele desistiu, portanto não podemos apagar o registro). E quando o mesmo usuário vai cadastrar outro usuário, caso antes tenha desistido, ao invés de criar um novo registro, ele busca o antigo e updateia.

Tá, esse exemplo do usuário eu nunca usei, mas não é incomum situações como essa, que preciso colocar um insert/update no mesmo método, dependendo deles.

[quote]Não é recomentado fazer um cache do banco na “mão”, tem frameworks para isto.
porque não/criar um cache: vc teria que implementar todo o controle de transação na mão, e garantir a integridade do cache, só em falar em “implementar uma cache” já teria um certo problema, pq é recomentado vc usar caches já prontas, que tenham todo uma infra já feita… [/quote]
Acontece que eu já dei uma olhada nos frameworks para cache, e nenhum que eu encontrei atendeu essa minha necessidade específica ou acabava sendo mais demorado implementar ele que criar esse HashMap.

Veja bem, eu quero, por exemplo, listar os 10 últimos registros inseridos numa determinada tabela, mas tenho umas 40000 requisições por minuto, não encontrei nenhuma solução melhor que fazer isso (visto que não são muitas queries, apenas umas 10 estáticas diferentes, e as informações não são necessariamente inseridas por essa aplicação java)

Se fosse algum outro caso, vi que um EntidadManager soluciona muito bem essa questão de cache.

Na verdade nem é tanto alta performance, é que estou estudando mesmo algumas formas de otimizar o desempenho de páginas web. No caso, eu vi que os sites de notícas (em PHP e ASP) serializam os objetos para XML para poder melhorar essa questão de acesso ao banco (no caso, eles esperam cerca de 40000 requisições por minuto). Mas tenho mesmo é experiência com banco de dados de BI, e uma tabela de uns 100.000.000 de registros acaba sendo bem lento para fazer uma consulta de uma linha… Por isso acabo sendo “crítico demais” com desempenho de aplicações menores. (heheh, principalmente por estar acostumado a otimizar consultas… Já fiz consultas de 5 horas cair para 10 minutos)

Ps: Lendo o link que vc me mandou, parece que o EntityManager tem mais funcionalidades que eu pensava. Mas achei ele meio que um “exagero” para esse caso que expliquei. Conhece algum método de uso dele ou algum framework que faça algo mais próximo do que eu expliquei?

[quote]Seguinte, esta estoria de pools, não é mais necessário “implementar” um do zero, todos os containers, applications servers que conheco tem um embutido… desta forma tme um cara que gerencia para vc o estas paradas… e sim, geralmente é legal vc configurar o pool para que ele tenha mais de uma conexão aberta… desta forma seu sistema não fica threadsafe em relação ao banco. [/quote]Legal, dei uma procurada sobre pool de conexões TOMCAT, e achei algumas coisas interessantes. Mas sabe me dizer se o que eu implemento, para o tomcat, por exemplo (sobre o pool de conexões) vai funcionar no glassfish, jboss, etc…?

Desculpe as perguntas serem “idiotas”, mas é que eu sou meio “fissurado” em melhorar performance e busco fazer o mais intuitivo possível. (tá, nem sempre consigo, mas é errando que se aprende)

EDIT: heheh, enquanto respondia (fazendo outras coisas, acabei demorando muito para responder) foram aparecendo novas respostas, vou atualizar esse post.

Acredito que um controle de transação seria mais custoso ao banco e o lock na tabela para leitura poderia permitir uma inserção de algum registro dependendo do banco não? Pelo que sei, alguns bancos travam as linhas retornadas do select, e não a tabela inteira, se não houve linhas retornadas não trava nada na tabela.

E se não é garantir a unicidade dos registros, qual a função de uma chave primária? Na maioria dos bancos ela é um índice UNIQUE, estruturado e clusterizado para reduzir o tempo de pesquisa por eles e garantir a unicidade. E sempre vi nas documentações dos SGBDs MySQL e Oracle (que possuem REPLACE INTO e MERGE INTO) que essa é a função do replace into e merge into, testar a inserção na chave primária para atualizar ou inserir o registro.

Mas concordo que para o que vem na camada da aplicação (no caso, JAVA), tem uma perda muito grande de desempenho por causa do pipeline dos processadores.

Vejam bem, não é raro eu inserir no banco uns arquivos de texto de uns 4GB ou mais (no geral uso o Kettle para isso, mas em casos isolados tenho que escrever uma rotina para corrigir os erros do arquivo, como datas com vários formatos, etc…) E já houve casos em que eu tive que inserir/atualizar um registro (vinha várias versões do mesmo registro em arquivos diferentes, como carregava da menor data para a maior, ficava sempre com o registro mais recente).

Foi aí que eu percebi que o Try/Catch fica mais rápido que o Update->insert (mas considere que é porque 80% era somente insert’s)
No mesmo caso, percebi que o replace into, no caso do mysql ficava mais lento que somente inserts em cerca de 20% do tempo. Não cheguei a testar no Oracle o Merge Into, porque mudei a minha forma de fazer esse processo inserindo tudo em uma tabela temporária antes (o que consome muito tempspace, mas é mais rápido principalmente quando temos que atualizar várias tabelas com a mesma informação)

Agora, baseado no desenvolvimento WEB, melhor mesmo abrir uma transação? não ficaria mais lento o ambiente em geral? tanto banco quanto servidor?

Se as informacoes nao sao inseridas somente pela aplicacao Java, como voce vai controlar o cache? Se alguma aplicacao externa alterar os dados, voce nao consegue “invalidar” o cache sem ter que ir ao banco de dados. E se voce precisar ir ao banco de dados toda vez que usar o cache, entao o cache nao faz sentido :slight_smile:

Acho que este eh o ponto a ser melhorado :slight_smile: Em “aplicacoes menores”, se voce tem um processo que leva 1 segundo, gasta 10 horas para otimiza-lo, e consegue 10% de melhoria, voce acaba tendo um processo que leva 900ms, o que eh imperceptivel para o usuario final. Se voce gastasse estas 10 horas otimizando a “user interface”, por exemplo, aposto que o usuario ficaria mais feliz :wink:

Nao fique fissurado em melhorar performance. Faca seu codigo “corretamente”, usando os recursos da maneira correta e voce nao precisara se preocupar com performance em 90% dos casos. Nos 10% dos casos em que voce estiver sofrendo com performance, voce pode otimizar pontos especificos da sua aplicacao, mas se preocupar com performance sem realmente ter um problema de performance eh desnecessario, desgastante e inutil na maioria das vezes :slight_smile: Em geral, tenta-se otimizar algo que nao precisa, muitas vezes pq nao se entende o que realmente se passa por baixo dos panos (nao estou falando especificamente do seu caso).

Se voce se refere somente a transacao de banco de dados (e nao transacoes JTA complexas, por exemplo), entao isso nao faz diferenca. Mesmo que vc tenha “auto commit”, a grande maioria dos bancos faz uma transacao implicita.

[quote]Se as informacoes nao sao inseridas somente pela aplicacao Java, como voce vai controlar o cache? Se alguma aplicacao externa alterar os dados, voce nao consegue “invalidar” o cache sem ter que ir ao banco de dados. E se voce precisar ir ao banco de dados toda vez que usar o cache, entao o cache nao faz sentido [/quote]Então, eu queria uma espécie de Timeout para indicar se uma consulta está atualizada ou não, como eu disse no exemplo de antes, se num site com 40.000 acessos por minuto eu reduzir para uma consulta dessas por minuto o desempenho vai subir muito.

E esse tipo de cache que ainda não consegui encontrar (talvez até por não procurar o suficiente ou da maneira correta).

[quote]Acho que este eh o ponto a ser melhorado Em “aplicacoes menores”, se voce tem um processo que leva 1 segundo, gasta 10 horas para otimiza-lo, e consegue 10% de melhoria, voce acaba tendo um processo que leva 900ms, o que eh imperceptivel para o usuario final. Se voce gastasse estas 10 horas otimizando a “user interface”, por exemplo, aposto que o usuario ficaria mais feliz [/quote]Concordo com isso, mas nesse caso, estou fazendo mais para aprender. Quando tiver que passar por uma situação dessas mesmo não precisar perder tempo depois de pronto procurando onde pode melhorar a performance. Digamos que melhorar a performance de uma ferramenta pesada WEB (no momento estou trabalhando com uma que usa o cubo OLAP da pentaho, em JSP mesmo), que como as informações são inseridas diariamente, estamos limpando o cache as 0:00 de cada dia (mas fazendo isso manualmente por enquanto, rodando um shell que dá um restart no tomcat)

[quote]Nao fique fissurado em melhorar performance. Faca seu codigo “corretamente”, usando os recursos da maneira correta e voce nao precisara se preocupar com performance em 90% dos casos. Nos 10% dos casos em que voce estiver sofrendo com performance, voce pode otimizar pontos especificos da sua aplicacao, mas se preocupar com performance sem realmente ter um problema de performance eh desnecessario, desgastante e inutil na maioria das vezes Em geral, tenta-se otimizar algo que nao precisa, muitas vezes pq nao se entende o que realmente se passa por baixo dos panos (nao estou falando especificamente do seu caso). [/quote]Mas isso acontece as vezes comigo sim(de otimizar algo que não precisa)!!! Principalmente porque estou mais habituado a banco de dados [grandes], e neles eu consigo otimizar bem as coisas. Como estou me adaptando ao Java WEB (antes de entrar em BI, programava em delphi e PHP, conceitos completamente diferentes).

Ainda conheço poucos frameworks JAVA, por isso estou estudando sobre eles! Para não “reinventar a roda” a cada sistema que eu vou construir. Como tenho determinados problemas no dia a dia, tento primeiro ver quais frameworks (ou mesmo se vou precisar implementar algo novo) para esses casos antes de começar a efetivamente usar o Java para todos os meus sistemas (tenho um site que estou fazendo e esse cubo OLAP por enquanto, mas o cubo OLAP tem um kernel de terceiros).

[quote]Se voce se refere somente a transacao de banco de dados (e nao transacoes JTA complexas, por exemplo), entao isso nao faz diferenca. Mesmo que vc tenha “auto commit”, a grande maioria dos bancos faz uma transacao implicita. [/quote]Vou dar uma olhada sobre transações JTA, é que como normalmente faço tudo no SQL digitado mesmo, normalmente faço um [START|BEGIN] transaction na linha de comando, e no máximo, quando necessário, um lock manual na tabela (mas por ser datawarehouse, dificilmente preciso fazer isso)

Aproveitando, queria fazer uma pergunta meio fora do assunto dessa parte do fórum:

Estou usando facelets, existe alguma forma de fazer o view handler do facelets gerar um cache em arquivo da página jsf? E qual framework eu devo usar para sobrescrever, por exemplo, um template que eu criei que tenha “#{nome_variavel/objeto/Etc…}”?

[quote=evefuji]…
E esse tipo de cache que ainda não consegui encontrar (talvez até por não procurar o suficiente ou da maneira correta).[/quote]@evefuji,
V6 já trocaram algumas idéias, discutiram…
Mas, seria + interessante se vc nos desse 1 descrição + detalhada sobre a Arquitetura de Software adotada, tecnologias utilizadas, etc.
P/ex.: se usa JDBC puro, ou Hibernate, ou JPA (c/ Hibernate), etc.[quote=evefuji]Eu tenho uma aplicação web em que eu cadastro um usuário, por exemplo, mas é possível, durante o cadastro, o usuário postar uma foto. Nesse caso, eu crio um registro temporário, com uma descrição temporária, no momento que ele clica no “novo usuário”, para que quando upar a foto, tenha um registro para a FK fazer referência … Mas acontece que um usuário pode desistir do cadastro no meio do processo (e nesse caso, nem sempre sabemos que ele desistiu, portanto não podemos apagar o registro). [/quote] Será q não seria o caso vc implementar então uma Camada ‘DomainService’ para esta funcionalidade, na qual vc poderia implementar 1 Transação (mas, não de Banco e sim de) de Negócio, quem sabe usando princípio DIP??!

[quote=evefuji]…Vou dar uma olhada sobre transações JTA, é que como normalmente faço tudo no SQL digitado mesmo, normalmente faço um [START|BEGIN] transaction na linha de comando, e no máximo, quando necessário, um lock manual na tabela (mas por ser datawarehouse, dificilmente preciso fazer isso)
…[/quote]Ah, tb existem 2 bons caches q vc poderia utilizar: o EhCache, ou o JBossCache! :wink:

Desculpe a demora em responder, acabei ficando sem tempo…

Acabei fazendo o pool de conexões com o do Tomcat mesmo…

Para conectar, uso JDBC puro mesmo. (ainda vou aprender o hibernate, mas no momento, não é a prioridade) Mas como eu disse, estou apenas buscando alternativas mais práticas ao que uso atualmente. Apenas aprendendo.

A aplicação que tenho, é um cubo OLAP, atualmente com Engine da Pentaho (se alguém conhecer alguma outra engine de cubo OLAP em JSP freeware ou mais barata que o do Pentaho eu agradeço), não é feito nenhuma gravação no banco de dados, somente consulta. Nesse caso, o cache está sendo limpado a cada 24h(frequencia com que as informações são atualizadas no datawarehouse).