Usando hiberante de forma otimizada

30
Usando Hibernate de Forma Otimizada Jadson Santos

description

Usado Hibernate de forma otimizada

Transcript of Usando hiberante de forma otimizada

Page 1: Usando hiberante de forma otimizada

S

Usando Hibernate de Forma Otimizada

Jadson Santos

Page 2: Usando hiberante de forma otimizada

Hibernate Otimizado

S  Hibernate S  O Hibernate é um framework para o mapeamento objeto-

relacional escrito na linguagem Java. Este framework facilita o mapeamento dos atributos entre uma base tradicional de dados relacionais e o modelo objeto de uma aplicação, mediante o uso de arquivos XML ou anotações Java.

Page 3: Usando hiberante de forma otimizada

Hibernate Otimizado

S  A principal promessa do Hibernate S  Quando a gente começa a estudar o Hibernate, a principal

promessa para vender o produto é: O Hibernate gera as chamadas SQL e libera o desenvolvedor de trabalhar com os dados de forma relacional, desde que os mapeamentos das classes de domínio tenham sido realizados, o desenvolvedor irá trabalhar apenas com objetos

S  Então oba S  Eu tenho o id da entidade e para recuperar seus dados eu vou no

meu Dao que usa Hibernate e faço:

Entidade e = dao.findById(1);

S  E tenho o objeto “e” populado, simples!

Page 4: Usando hiberante de forma otimizada

Hibernate Otimizado

S  O problema é que essa promessa não é cumprida S  Na prática, apenas se for uma entidade de domínio com

poucos dados e sem relacionamento com outras entidades do sistema isso vai funcionar

public class TipoAluno{

@Id

private int id;

@Column(name = "descricao")

private String descricao; }

Page 5: Usando hiberante de forma otimizada

Hibernate Otimizado

S  Normalmente no seu sistema você terá apenas 10 ou 20 tipos de alunos e serão sempre recuperados apenas o id e a descrição

S  Para entidades que possuem dezenas de milhares de linhas na tabela para cima, e que possuam muitos relacionamentos essa abordagem deixar o seu sistema lendo e não escalável.

Page 6: Usando hiberante de forma otimizada

Hibernate Otimizado

public class Aluno{ @ManyToOne

@JoinColumn(name = "id_tipo_aluno")

private TipoAluno tipo;

@ManyToOne(fetch = FetchType.EAGER)

@JoinColumn(name ="id_discipina”)

private Disciplina discipinas; }

Page 7: Usando hiberante de forma otimizada

Hibernate Otimizado

S  O que vai ocorrer se você for usar “orientação a objetos” e fizer um findById na entidade Aluno?

S  O Hibernate vai recuperar todas as informações do Aluno, +todas as informações dos tipo de alunos + todas as informações das disciplinas associadas ao aluno + todas as informações das entidades associadas a disciplinas e assim sucessivamente, até o nível máximo definido nas configurações do Hibernate

S  Multiplique por 1000 usuários fazendo essa consulta. Já viu o problema ne?

Page 8: Usando hiberante de forma otimizada

Hibernate Otimizado

S  Já sei! Troco o mapeamento de EAGER para LAZY.

S  Resolve em parte, o Hibernate não irá trazer os dados dos relacionamentos, porém a cada vez que você fizer aluno.getDisciplinas() com a sessão aberta ele gerará N consultas extras para recuperar as disciplinas. Onde N é o número de disciplinas no aluno.

S  Se no sistema existem 1000 alunos consultando suas disciplinas e, em média, cada aluno está matriculado em 5 disciplinas, em vez de 1.000 consultas banco, o Hibernate gerará 1.000 iniciais + 5.000 consultas para recuperar informações da disciplinas

Page 9: Usando hiberante de forma otimizada

Hibernate Otimizado

S  Se a sessão do Hiberante já estiver fechada, você passará por um problema bastante conhecido de LazyInitializationException. E um erro será gerado para o usuário

Page 10: Usando hiberante de forma otimizada

Hibernate Otimizado

S  Então como eu resolvo isso?

S  Simples! Mapeie tudo o LAZY, faça projeção das suas consultas, recupere apenas os dados necessários e popule o seu objeto.

SELECT aluno.id, aluno.nome, tipo.descricao

FROM Aluno aluno

INNER JOIN aluno.tipo tipo

WHERE aluno.id = :idAluno

Page 11: Usando hiberante de forma otimizada

Hibernate Otimizado

S  Essa consulta irá retornar uma lista de arrays de objetos

List<Object[]> dados= q.list();

S  Existem alguns métodos utilitário que podem ser usado para popular os objetos a partir a lista retorna pela consulta

Page 12: Usando hiberante de forma otimizada

Hibernate Otimizado

S Isso resolverá 90% dos problemas de performance do seu sistema

S Mas é muito trabalho? Eu sei! mas você quer qualidade no seu sistema ou não?

Page 13: Usando hiberante de forma otimizada

Hibernate Otimizado

S  Quando eu falo em adicionar projeção são significa fazer isso:

SELECT aluno.*

FROM Aluno aluno

WHERE aluno.id = :idAluno

S  Isso não é fazer projeção!

Page 14: Usando hiberante de forma otimizada

Hibernate Otimizado

S  E os outros 10% ? S  Esses 10% entram os caos de uso que são muitos usado ou

casos de uso onde o requisito de performance é fundamental

S  Nestes casos você deve utilizar diretamente SQL e tem que “escovar os bits” da sua consulta SQL para ver como ela pode ser otimizada

S  O Hibernate nesses casos dá nem pro gasto.

Page 15: Usando hiberante de forma otimizada

Hibernate Otimizado

S  Como eu sei o que otimizar?

S  Por exemplo, no PostgreSQL existe uma opção chamada “Explain Query" que mostra como o banco está realizando a sua consulta

S  No exemplo a seguir a consulta SQL deveria retorna os 20 primeiros resultados de um JOIN entre duas tabelas

Page 16: Usando hiberante de forma otimizada

Hibernate Otimizado

SELECT s.id_servidor, p.id_pessoa

FROM rh.servidor

JOIN JOIN comum.pessoa p ON....

WHERE .....

LIMIT 20

Page 17: Usando hiberante de forma otimizada

Hibernate Otimizado

S  Apesar da quantidade de dados retornado ser pequena, o limite só pode ser aplicado depois que as condições do JOIN foram testadas, então o Banco de dados irá realizar o Seq Scan em todas as tabelas para realizar o JOIN.

S  Seq Scan em tabelas grandes sempre é um problema

S  Isso quer dizer que ele precisa percorrer varias tabelas com milhares de resultados para realizar a consulta. O que demora em média 500ms.

S  Super rápido, mas pense que 100 usuários podem estar utilizando essa consulta, então são 50 segundos em produção de uso do banco de dados.

Page 18: Usando hiberante de forma otimizada

Hibernate Otimizado

Page 19: Usando hiberante de forma otimizada

Hibernate Otimizado

S  Como eu só quero um conjunto pequeno de dados, a saída foi realizar uma sub consulta menor antes, que restringisse o meu conjunto de resultados antes de realizar o JOINs da consulta principal

Page 20: Usando hiberante de forma otimizada

Hibernate Otimizado SELECT s.id_servidor, p.id_pessoa

FROM rh.servidor

JOIN JOIN comum.pessoa p ON....

WHERE s.id_servidor IN ( -------------------------------------------------

--- Otimização, só recupera desse conjunto pequeno de dados ---

SELECT s.id_servidor FROM ... WHERE ...

LIMIT 100

-------------------------------------------------

)

LIMIT 20

Page 21: Usando hiberante de forma otimizada

Hibernate Otimizado

S  Isso fez o tempo da consulta cair de 500ms para 17ms. Que para 100 usuários o tempo total de uso do banco cai de 50 segundos para 1,7segudos.

Page 22: Usando hiberante de forma otimizada

Hibernate Otimizado

S  Outro exemplo de otimização de consultas SQLs

S  Tenho um problema onde se deveria verificar se um usuário estava entre os resultados retornados por uma determina consulta.

S  Como isso foi realizado a princípio?

Page 23: Usando hiberante de forma otimizada

Hibernate Otimizado

SELECT count(id_pessoa)

FROM comum.pessoa p

WHERE 123456 – id da pessoa a ser testado

IN(

--- consulta interna

select p.id_pessoa FROM ....

)

Page 24: Usando hiberante de forma otimizada

Hibernate Otimizado

S  Porém essa consulta interna pode retornar um número muito grande dados, na ordem de 2.000.000 de ids para verificar se o usuário que você quer, está dentro desse conjunto

S  Resultado: 1 segundo a consulta x 100 usuários : 100 segundos de uso do banco.

S  Será que não existe uma maneira mais otimizada de fazer isso?

Page 25: Usando hiberante de forma otimizada

Hibernate Otimizado

S  Se você parar para pensar um pouco, verá que se eu quero verificar se um determinado usuário está no grupo da consulta é só verificar se:

( count(*) consulta AND meu usuário ) > 0

Page 26: Usando hiberante de forma otimizada

Hibernate Otimizado

SELECT count(interna.id_pessoa)

FROM (

-- consulta anterior

select p.id_pessoa

FROM comum.pessoa p

INNER JOIN ....

-- e meu usuário

AND p.id_pessoa = 123456

) as interna

Page 27: Usando hiberante de forma otimizada

Hibernate Otimizado

S  Resultado: De 1 segundo o tempo da consulta caiu para 17ms , para 100 usuários : 1,7 segundos de uso do banco.

+- 5.800 % de ganho do tempo da consulta. Só isso. É pouco?

Page 28: Usando hiberante de forma otimizada

Hibernate Otimizado

S  Conclusões S  O Hibernate se tornou o framework padrão para

mapeamento objeto relacional, porém o seu mau uso trás sérios problemas de performance ao sistema.

S  O mapeamento dos relacionamentos do domínio como LAZY e a recuperação apenas das informações necessárias ao seu caso de uso resolve 90% das questões de performance do seu sistema

S  Os demais 10%, onde o requisito de performance é fundamental, deve abandonar o Hibernate e analisar os pontos mais demorados da sua consulta e tentar descobrir maneiras de otimizá-la

S  Esses 10% vai muito da experiência e habilidade do desenvolvedor

Page 29: Usando hiberante de forma otimizada

Hibernate Otimizado

S  Conclusões S  Falei apenas das otimizações em relação ao tempo das

consultas, existe ainda outras maneiras de otimizar consultas como criar cache em memória ou desnormalização das tabelas do banco de dados. Ou ainda, em casos específicos, o uso de banco de dados não relacionais.

Page 30: Usando hiberante de forma otimizada

S

Usando Hibernate de Forma Otimizada

Jadson Santos