Processando em lote com Hibernate

O Hibernate é um poderoso framework que facilita a maioria do serviços desenvolvidos que são relacionados a banco de dados, no entanto, devemos entender algumas estratégias que o Hibernate executa durante seus procedimentos, principalmente quando vamos trabalhar com lote de dados, pois ele pode facilitar bastante o processo mas pode comprometer a performance se não entendermos corretamente o que será executando durante a execução de uma grande quantia de dados subsequentes.

Cache de primeiro nível

A principal funcionalidade que devemos entender é o cache de primeiro nível do Hibernate porque todo objeto que será persistido é adicionado no cache de primeiro nível até que a sessão seja liberada, ou seja, caso tiver 1000 registros para serem processados os 1000 objetos serão alocados em memória até o commit da transação onde será limpado a sessão.

Esse é o comportamento padrão do Hibernate, podemos ver que essa estratégia irá funcionar independente do cenário e não terá problemas com processamento com poucos registros, no entanto, é visível que esse cenário pode se agravar conforme o aumento de registros processados.

Limpando a sessão manualmente

A forma de minimizar essa degradação na performance é limpando a sessão conforme uma quantia de dados processados, que vai variar de acordo com o cenário do projeto.

A limpeza da sessão é feita através do método clear do entityManager (ou da session utilizada no momento), mas antes de limpar esses dados do cache precisamos executar o flush para sincronizar esses dados para que não sejam perdidos.

Exemplo:


@Transactional
public void processar() {
  
final List<Entidade> entidades = servico.buscarEntidadesParaAtualizar();

  final int TAMANHO_LOTE = 30; 
  int i = 0;
  for(Entidade entidade : entidades) {

    //executa alguma alteração na entidade
    entidade.processar();
    entityManager.merge(entidade);

    if ( i % TAMANHO_LOTE == 0 ) {
        entityManager.flush();
        entityManager.clear();
  }
}
  • TAMANHO_LOTE: Pode ser de 10 a 50, vai depender de quanto de memória o processamento pode consumir da aplicação.
  • entityManager.merge: Realiza o update na base de dados, mas só será executado de fato na chamada do flush.
  • entityManager.flush(): Sincroniza o contexto de persistência com a base de dados, ou seja, executa os scripts na dentro na transação.
  • entityManager.clear(): Limpa o contexto de persistência, inclusive o cache de primeiro nível e os scripts SQLs que estavam em memória aguardando o final da execução.

Configurando batch_size do Hibernate

Outra solução para melhorar a performance é configurar o batch size do Hibernate, que é uma propriedade que ativa a inserção/atualização em lote no hibernate e determina o número de inserções/atualizações que serão executada em cada lote, isso é necessário para evitar OutOfMemoryException durante a execução do processamento, pois além do cache de primeiro nível o Hibernate também insere todas entidades em um cache a nível de sessão e configurando essa propriedade ele limpará esse dados de acordo com o tamanho do size definido na configuração.

persistence.xml

...
 <properties>
     <property name="hibernate.jdbc.batch_size" value="30" />
 </properties>

A documentação do hibernate recomenda o tamanho de 10-50.

Obs: É uma recomendação da documentação do Hibernate utilizar o mesmo valor definido no batch_size nas operações em batch que utilizam flush/clear.

Performance em processamentos em lote…

Também há outras formas de melhorar a performance dos processamentos em lotes, mas aí já estão relacionadas a diferentes implementações como: paginar o processamento,refinar as consultas que antecedem o processamento, trabalhar com VO(Value Object) ao invés de entidades ou até mesmo chamar SQL nativos em casos específicos.

Anúncios

Deixe um comentário

Preencha os seus dados abaixo ou clique em um ícone para log in:

Logotipo do WordPress.com

Você está comentando utilizando sua conta WordPress.com. Sair /  Alterar )

Foto do Google+

Você está comentando utilizando sua conta Google+. Sair /  Alterar )

Imagem do Twitter

Você está comentando utilizando sua conta Twitter. Sair /  Alterar )

Foto do Facebook

Você está comentando utilizando sua conta Facebook. Sair /  Alterar )

Conectando a %s

This site uses Akismet to reduce spam. Learn how your comment data is processed.