Artigo no estilo: Curso
Por que eu devo ler este artigo:Complementando o primeiro artigo, no qual discutimos como funciona o Cache de 2º Nível (C2N) no Hibernate, nesta segunda parte detalharemos o funcionamento do EhCache quando configurado para atuar em disco. Embora trivial para se configurar, um cache em disco comporta-se de maneira bem diferente de um cache puramente em memória, gerando picos de alocação de objetos que podem vir a ser inesperados quando se deseja manter a latência de uma aplicação sob controle. Ao longo do artigo iremos explorar como se comportam as estruturas do C2N quando serializadas para um arquivo e discutiremos técnicas para melhorar o desempenho na leitura desses dados.

Na primeira parte do artigo vimos como configurar o EhCache para atuar como C2N do Hibernate e exploramos diferentes opções de configurações inerentes a API do Hibernate que devem ser refletidas na configuração do cache, por exemplo, opções transacionais no modo de acesso à entidade e definição de regiões em que serão armazenadas as consultas e entidades.

Nesta segunda parte do artigo faremos algumas considerações com respeito à utilização de caches em disco que, embora transparentes do ponto de vista de configuração, introduz novos aspectos aos quais devemos estar atentos antes de decidir se vamos fazer uso de caches em disco ou não.

Duas das principais razões que podem nos levar a refletir se devemos ou não armazenar dados em disco é a quantidade de memória disponível para a JVM e a quantidade de objetos que ficarão armazenados em cache. No primeiro caso, a preocupação principal é se o uso do cache pode tomar espaço de memória que deveria ser alocado para outros componentes necessários para a execução da aplicação.

No segundo, a preocupação é se o número de objetos ocupará espaço suficiente para colocar pressão no Garbage Collector, degradando o desempenho geral da aplicação. Como sempre, em se tratando de cache, a melhor solução para esses problemas depende tanto do conhecimento do hardware como da aplicação.

Entretanto, ao começar a utilizar caches em disco, é possível identificar estruturas e padrões que geram custos para a aplicação, mas que podem ser atenuados, conforme veremos ao longo do artigo. Antes de discutir as estratégias que utilizaremos para melhorar o desempenho do C2N do Hibernate, precisamos primeiro entender como funciona o EhCache quando passamos a descarregar o conteúdo da memória para o disco.

Camadas de armazenamento

O EhCache define como Camada de Armazenamento (Storage Tier) a região física onde ficarão armazenados os dados contidos em um cache. Uma configuração standalone do EhCache permite que as entradas do cache (chave e valor) sejam armazenadas em três camadas. Em ordem decrescente de velocidade de acesso, essas camadas são enumeradas como:

1. Heap Memory: as entradas são armazenadas como objetos Java “normais” e são diretamente acessíveis pela JVM;

2. Off Heap Memory: as entradas são armazenadas de forma serializada em estruturas do tipo java.nio.ByteBuffer. Essa classe foi introduzida no JDK 1.4 como a estrutura fundamental para operações de input/output realizadas na API de NIO, fazendo o papel dos clássicos Streams e arrays de bytes.

O diferencial de ByteBuffer é que ele pode armazenar uma quantidade grande de memória (até 2GB por instância) nativa. Quando um buffer é criado através do método allocateDirect(), a JVM aloca blocos de memória que não são gerenciados como os outros objetos, no sentido que não participam dos ciclos de garbage collection, o que permite que aplicações façam uso de centenas de GB de memória sem sofrer pausas devido à coleta de lixo;

3. Disk: as entradas são armazenadas de forma serializada em arquivos.

Neste artigo nos limitaremos a falar da camada de armazenamento do tipo Disk, porém os conceitos discutidos podem ser aplicados para a camada Off Heap Memory, que requer uma licença para ser utilizada.

O EhCache permite que todos os níveis sejam adotados em um único cache e trabalha para manter os dados mais frequentemente acessados nas camadas cujo acesso é mais rápido. Por exemplo, se um cache é configurado para conter até 100 elementos em memória (heap memory) e 100.000 no disco, ao longo da utilização do cache, a tendência é que os 100 elementos requisitados com maior frequência passem a residir na camada de acesso mais rápido.

A Figura 1 mostra o processo de consulta no cache quando configurado com mais de uma camada de armazenamento. O EhCache sempre procura na camada de acesso mais rápido e caso não encontre, efetua a mesma pesquisa nas camadas de acesso mais lento (disco).

Consulta no cache com Armazenamento Off-Heap

Figura 1. Consulta no cache com Armazenamento Off-Heap e em Disco.

Neste diagrama estão caracterizadas duas outras etapas que ocorrem após consultas efetuadas com sucesso, a atualização de estatísticas e a migração. A atualização de estatísticas consiste em marcar quantas vezes um elemento foi acessado e o último momento em que foi acessado. Quando um elemento é encontrado em disco e o cache atingiu o número máximo de elementos em memória, ocorrerá a migração de um dos elementos em memória para o disco e vice-versa.

Para determinar qual elemento da memória deve migrar para o disco, o EhCache aplica uma política de expurgo em uma amostra do conjunto de dados em memória. Por exemplo, se a política de expurgo for do tipo LRU (padrão), a “vítima” escolhida será o elemento que foi acessado menos recentemente. Por outro lado, se a política for LFU, o elemento escolhido será o que tiver o menor número de acessos.

É importante ressaltar que a migração é baseada em pequenas amostragens aleatórias, o que implica que não necessariamente o melhor candidato será migrado, porém também diminu ...

Quer ler esse conteúdo completo? Tenha acesso completo