Colección de citas famosas - Slogan de motivación - Configuración detallada de la caché de hibernación

Configuración detallada de la caché de hibernación

Muchas personas no saben mucho sobre el caché de segundo nivel o no lo entienden bien. Siempre quise escribir un artículo sobre el caché de segundo nivel de hibernación, pero hoy finalmente no pude evitarlo.

Mi experiencia proviene principalmente de hibernate2.1, y los principios básicos son los mismos que los de 3.0 y 3.1. Por favor perdona mi terquedad.

La sesión de Hibernate proporciona un caché de primer nivel. En cada sesión se carga dos veces el mismo id y los dos sql no se envían a la base de datos. Sin embargo, cuando se cierra la sesión, la caché de primer nivel quedará invalidada.

La caché de segundo nivel es una caché global en el nivel de fábrica de sesiones. Puede utilizar diferentes bibliotecas de almacenamiento en caché, como ehcache, oscache, etc. y es necesario configurar hibernate.cache.provider_class. Usamos ehcache aquí, está en 2.1.

hibernate .cache .proveedor _ class = net .hibernate .ehcacheprovider

Si usa caché de consultas, agregue

hibernate cache . query _ cache = true

Un caché puede considerarse simplemente como un mapa, donde los valores se pueden encontrar en el caché por clave.

Caché de clase

Para un registro, es decir, un PO, se encuentra en función del ID. La clave del caché es el ID y el valor es el POJO. Ya sea una lista, una carga o una iteración, cada vez que se lee un objeto, la memoria caché se llena. Sin embargo, la lista no utilizará el almacenamiento en caché, iterar primero obtendrá los identificadores de selección de la base de datos y luego los cargará un identificador a la vez. Si hay uno en el caché, lo obtendrá del caché; de lo contrario, irá a la carga de la base de datos. Suponiendo que se trata de una caché de lectura y escritura, debe configurar:

<cache-using="read-write"/>

Si la implementación de caché de segundo nivel que está utilizando es ehcache, entonces es necesario configurar ehcache.xml.

& ltcache name = "com .

Eternal indica si el caché nunca expirará y timeToLiveSeconds es el tiempo de espera para cada elemento en el caché (aquí hay un POJO). Si es eterno = "falso", el elemento se eliminará cuando se exceda el tiempo especificado. TimeToIdleSeconds es el tiempo de inactividad y es opcional. Cuando hay más de 500 elementos colocados en el caché, si se desborda el disco = "verdadero", algunos datos del caché se guardarán en archivos temporales en el disco duro.

Cada clase que deba almacenarse en caché debe configurarse así. Si no lo configura, hibernación le avisará al inicio y luego usará la configuración de defaultCache para que varias clases * * * disfruten de una configuración.

Cuando hibernate modifica una ID, hibernate conocerá y eliminará el caché.

De esta manera, puedes pensar que para la misma condición de consulta, la primera lista, la segunda iteración, puedes usar el almacenamiento en caché. De hecho, esto es difícil porque no se puede saber cuándo es la primera vez y las condiciones para cada consulta suelen ser diferentes. Si hay 100 registros en la base de datos y los ID varían de 1 a 100, se enumeran los primeros 50 ID, pero la segunda iteración consulta los ID 30-70 y luego 30-50. Por eso siempre siento que la iteración es inútil y siempre habrá problemas 1 + N.

(Digresión: se dice que las consultas a gran escala con listas cargarán todo el conjunto de resultados en la memoria, lo cual es muy lento, mientras que iterar solo selecciona identificadores, pero las consultas a gran escala siempre requieren paginación, y nadie lo hará Para cargar realmente todo el conjunto de resultados Si hay 20 artículos en una página, iterate*** necesita ejecutar 21 declaraciones, aunque la lista selecciona varios campos, pero es más lento que la primera declaración de identificación seleccionada en iterar. declaración No es necesario cargar todo el conjunto de resultados, y la hibernación también optimizará según el dialecto de la base de datos, como el uso de limitaciones de MySQL. En general, debería ser más rápido que list)

Si lo desea. almacenar en caché la lista o iterar los resultados de la consulta requieren el uso de caché de consultas.

Caché de consultas

Primero debes configurar la caché de hibernación.

Si usa ehcache y configura ehcache.xml, tenga en cuenta que hibernate3.0 ya no es el nombre del paquete net.sf

<cache name="net.SF.hibernate. cache.standardquerycache"

maxElementsInMemory="50" eterna="false" timeToIdleSeconds="3600"

timeToLiveSeconds="7200" desbordamiento de disco="true"/& gt.

& ltcache name="net.SF.hibernate.cache.updatetimestampscache"

maxElementsInMemory="5000" eterna="true" overflow todisk="true"/& gt; /p>

Entonces

query.set cacheable(true); //Activar caché de consultas

query. setcache region("mycache region"); caché utilizado, esto es opcional.

La segunda línea especifica que el caché que se utilizará es una región de caché, lo que significa que puede configurar el caché por separado para cada consulta. Para hacer esto necesitas configurarlo en ehcache.xml:

<cache name="mycache region" maxElementsInMemory="10" eterna="false" timeToIdleSeconds="3600" timeToLiveSeconds="7200" overflow todisk= "true"/>

Si se omite la segunda línea y no se establece cacheRegion, la configuración de la caché se consultará utilizando los criterios anteriores, es decir, net. SF. hibernación. cache. StandardQueryCache.

Para el almacenamiento en caché de consultas, la clave almacenada en caché es el sql generado en función de hql, más parámetros, paginación y otra información (se puede ver a través de la salida del registro, pero su salida no es muy legible, es mejor cambiar su código).

Por ejemplo, hql:

¿Cuál es el nombre c.de la clase c?

Genere sql aproximadamente de la siguiente manera:

¿Seleccione c.name de la clase c?

El parámetro es "tiger%", por lo que la clave de caché de consulta* es una cadena como esta (la escribí en la memoria, lo cual no es exacto, pero deberías entenderla después de leerla):

Seleccionar c.name de la clase c? , Parámetro: tigre%

De esta forma se garantiza la misma clave bajo la misma consulta, los mismos parámetros y otras condiciones.

Ahora hablemos de los valores almacenados en caché. Si está en modo de lista, el valor aquí no es el conjunto de resultados completo, sino el ID consultado.

En otras palabras, ya sea el método de lista o el método de iteración, cuando realizan la primera consulta, sus métodos de consulta son los mismos que de costumbre. list ejecuta un sql, iterate ejecuta 1+N, el comportamiento adicional es que llenan el caché. Sin embargo, al realizar la consulta por segunda vez en las mismas condiciones, el comportamiento es el mismo que iterar. Según la clave almacenada en caché, el valor se encuentra en el caché y luego se carga en el caché de la clase uno por uno. Esto se hace para ahorrar memoria.

Como puede ver, el almacenamiento en caché de consultas requiere que el caché de clase de la clase relevante esté activado. Cuando los métodos list e iterate se ejecutan por primera vez, se llenan tanto el caché de consultas como el caché de clases.

Hay otro problema importante que se pasa por alto fácilmente, es decir, ¡incluso el método de lista puede encontrar un problema 1+N después de activar el caché de consultas! Cuando se enumera la misma condición por primera vez, debido a que no se encuentra en el caché de consultas, independientemente de si hay datos en el caché de clases, siempre se envía una declaración SQL a la base de datos para obtener todos los datos, y luego la caché de consultas y la clase son cachés poblados. Pero el problema surge al ejecutarlo por segunda vez. Si el tiempo de espera de la memoria caché de su clase es muy corto y ahora la memoria caché de la clase se agotó, pero la memoria caché de consultas aún está allí, entonces el método de lista cargará la base de datos una por una después de obtener la cadena de identificación. Por lo tanto, el tiempo de espera del caché de clases no debe ser más corto que el tiempo de espera establecido por el caché de consultas. Si también establece un tiempo de aturdimiento, asegúrese de que el tiempo de aturdimiento de la caché de clases también sea mayor que el tiempo de supervivencia de la caché de consultas. Hay otras situaciones aquí, como que el programa desaloja por la fuerza el caché de clases, así que preste atención a esta situación usted mismo.

Además, si la consulta hql contiene select, el valor en la caché de la consulta es el conjunto de resultados completo.

Cuando Hibernate actualiza la base de datos, ¿cómo sabe qué cachés de consultas actualizar?

Hibernate mantiene la hora de la última actualización de cada tabla en un solo lugar, que en realidad se coloca en la configuración de caché especificada por la red. SF. hibernación. cache. Actualice TimeStampsCache arriba.

Al actualizar mediante hibernación, hibernación sabrá qué tablas se verán afectadas por esta actualización. Luego actualice la hora de la última actualización de estas tablas. Cada caché tiene una tabla de tiempos de generación y consultas para ese caché. Cuando la hibernación consulta si el caché existe, también eliminará el tiempo de generación del caché y las tablas consultadas por el caché, y luego encontrará la hora de la última actualización de estas tablas. Si la tabla se actualiza después del tiempo de generación, el caché no es válido.

Se puede ver que mientras se actualice una tabla, cualquier caché de consultas que involucre esa tabla no será válida, por lo que la tasa de aciertos de la caché de consultas puede ser muy baja.

Caché de colección

Debe configurarse en la colección hbm.

& ltcache use="read-write"/& gt;

Si la clase es Cat y la colección son hijos, la configuración en ehcache

& nombre ltcache = "com. "true"/& gt.

El caché de la colección es la misma lista que la consulta anterior almacenada en caché, pero solo mantiene una cadena de identificación, pero no se invalida porque la tabla ha sido actualizada. La caché de la colección solo se invalida cuando se agrega o elimina un elemento de la colección.

Hay una pregunta. Si su colección está ordenada en función de un campo, cuando uno de los elementos actualiza ese campo, el orden cambia y el orden en la memoria caché de la colección no se actualiza.

Estrategia de almacenamiento en caché

Caché de solo lectura: Nada que decir.

Caché de lectura-escritura: actualiza los datos que pueda necesitar el programa.

Caché de lectura/escritura suelta: los datos deben actualizarse, pero la posibilidad de que dos transacciones actualicen el mismo registro es muy pequeña. El mejor rendimiento es el caché de lectura y escritura no estricto.

Caché transaccional: el caché admite transacciones. Cuando ocurre una excepción, el caché también se puede revertir. Solo es compatible con el entorno JTA. No he investigado mucho sobre el entorno JTA.

La diferencia entre el caché de lectura y escritura y el caché de lectura y escritura suelto es que cuando el caché de lectura y escritura actualiza el caché, los datos en el caché serán reemplazados por el bloqueo. Si otras transacciones obtienen los datos almacenados en caché correspondientes y descubren que están bloqueados, obtendrán directamente la consulta de la base de datos.

En la implementación de ehcache en hibernate2.1, si ocurre una excepción en la transacción que bloquea parte del caché, el caché se bloqueará hasta que se agote el tiempo de espera después de 60 segundos.

El almacenamiento en caché de lectura y escritura no estricto no bloquea los datos en el caché.

Requisitos previos para usar la caché L2

Su programa de hibernación tiene acceso de escritura exclusivo a la base de datos. Es imposible que hibernación sepa que otros procesos han actualizado la base de datos. Tienes que operar la base de datos directamente a través de hibernación. Si usted mismo llama al procedimiento almacenado o usa jdbc para actualizar la base de datos, hibernate no lo sabe. Las actualizaciones y eliminaciones a gran escala en hibernación 3.0 no actualizarán el caché de segundo nivel, pero se dice que 3.1 resolvió este problema.

Esta restricción es bastante complicada. A veces, las actualizaciones y eliminaciones por lotes en hibernación son lentas, pero no puede escribir jdbc usted mismo para optimizarlo. Muy deprimente.

SessionFactory también proporciona un método para eliminar el caché. Si tiene que escribir algo de JDBC usted mismo, puede llamar a estos métodos para eliminar el caché. Los métodos son:

salida nula (clase persistenteClass)

Borrar todas las entradas del caché de segundo nivel.

void exit(class persistenteClass, serializableid)

Expulsa una entrada de la caché L2.

void Exit Collection (String roleName)

Borrar todas las entradas del caché de segundo nivel.

Colección de desalojo nulo (nombre de función de cadena, ID serializable)

Desaloja una entrada de la caché L2.

void exit query()

Borrar cualquier conjunto de resultados de consulta almacenado en caché en el área de caché de consultas predeterminada.

void eject query(string cacheRegion)

Expulsa cualquier conjunto de resultados de consulta almacenado en caché en la región de caché de consultas especificada.

Pero no lo recomiendo porque es difícil de mantener. Por ejemplo, si actualiza por lotes una tabla usando JDBC, tres cachés de consultas usarán la tabla, tres cachés de consultas se eliminarán usando evit query (caché de cadenas) y luego el caché de clases se eliminará usando evit (Clase persistente). que parece estar completo. Pero un día que agregue un caché de consultas relacionadas, es posible que se olvide de actualizar el código de eliminación aquí. Si su código jdbc está en todas partes, cuando agrega almacenamiento en caché de consultas, ¿sabe dónde más realizar los cambios correspondientes?

-

Resumen:

No asuma que el almacenamiento en caché mejorará definitivamente el rendimiento, siempre que pueda controlarlo y las condiciones sean las adecuadas. Hibernate tiene muchas limitaciones de caché de segundo nivel, por lo que es inconveniente usar jdbc, lo que puede reducir en gran medida el rendimiento de la actualización. Si lo usa indiscriminadamente si no comprende el principio, pueden ocurrir problemas 1+N N. El uso inadecuado también puede provocar la lectura de datos sucios.

Si no soportas las limitaciones de la hibernación, deberías realizar tu propio almacenamiento en caché a nivel de aplicación.

Cuanto mayor sea el nivel de caché, mejor será el efecto. Es como si la base de datos tuviera su propio caché, incluso si el disco tiene caché, nuestra aplicación aún tiene que almacenarlo en caché, incluso si la base de datos tiene caché. Debido a que el caché de bajo nivel no sabe qué hará el caché de alto nivel con estos datos, solo puede ser general. El caché de alto nivel puede implementar el almacenamiento en caché de manera específica, por lo que el almacenamiento en caché en un nivel superior lo será. mejor.