Consejos de Java Persistence de alto rendimiento

En este artículo, le mostraremos varios consejos de optimización de persistencia de Java de alto rendimiento que lo ayudarán a aprovechar al máximo su capa de acceso a datos.

Una capa de acceso a datos de alto rendimiento requiere mucho conocimiento sobre los componentes internos de la base de datos, JDBC, JPA, Hibernate, y esta publicación resume algunas de las técnicas más importantes que puede utilizar para optimizar su aplicación empresarial.

1. Registro de sentencias SQL

Si está utilizando un marco que genera declaraciones en su nombre, siempre debe validar la eficacia y eficiencia de cada declaración ejecutada . Un mecanismo de afirmación en tiempo de prueba es incluso mejor porque puede detectar problemas de consulta N + 1 incluso antes de enviar su código.

2. Gestión de la conexión

Las conexiones a la base de datos son caras, por lo que siempre debe utilizar un mecanismo de agrupación de conexiones .

Debido a que la cantidad de conexiones viene dada por las capacidades del clúster de base de datos subyacente, debe liberar las conexiones lo más rápido posible.

En el ajuste del rendimiento, siempre hay que medir, y establecer el tamaño correcto de la piscina no es diferente. Una herramienta como FlexyPool puede ayudarlo a encontrar el tamaño correcto incluso después de implementar su aplicación en producción.

3. Procesamiento por lotes JDBC

El procesamiento por lotes de JDBC nos permite enviar múltiples declaraciones SQL en un solo viaje de ida y vuelta a la base de datos. La ganancia de rendimiento es significativa tanto en el lado del controlador como en la base de datos. 

PreparedStatements
son muy buenos candidatos para el procesamiento por lotes, y algunos sistemas de bases de datos (por ejemplo, Oracle) admiten el procesamiento por lotes solo para declaraciones preparadas.

Dado que JDBC define una API distinta para el procesamiento por lotes (por ejemplo, 

PreparedStatement.executeBatch
), si está generando declaraciones manualmente, debe saber desde el principio si debe utilizar el procesamiento por lotes o no. Con Hibernate, puede cambiar al procesamiento por lotes con una sola configuración .

Hibernate 5.2 ofrece procesamiento por lotes a nivel de sesión , por lo que es aún más flexible en este sentido.

4. Almacenamiento en caché de declaraciones

El almacenamiento en caché de declaraciones es una de las optimizaciones de rendimiento menos conocidas que puede aprovechar fácilmente. Dependiendo del controlador JDBC subyacente, puede almacenar 

PreparedStatements
en caché tanto en el lado del cliente (el controlador) como en el lado de las bases de datos (ya sea en el árbol de sintaxis o incluso en el plan de ejecución).

5. Identificadores de hibernación

Cuando se usa Hibernate, el 

IDENTITY
generador no es una buena opción ya que deshabilita el procesamiento por lotes de JDBC.

TABLE
El generador es aún peor, ya que utiliza una transacción separada para obtener un nuevo identificador, lo que puede presionar el registro de transacciones subyacente, así como el grupo de conexiones, ya que se requiere una conexión separada cada vez que necesitamos un nuevo identificador.

SEQUENCE
es la elección correcta, e incluso admite SQL Server desde la versión 2012. Para 
SEQUENCE
identificadores, Hibernate ha estado ofreciendo optimizadores como pooled o pooled-lo que pueden reducir el número de viajes de ida y vuelta de la base de datos necesarios para obtener un nuevo valor de identificador de entidad.

6. Elegir los tipos de columna adecuados

Siempre debe usar los tipos de columna correctos en el lado de la base de datos. Cuanto más compacto sea el tipo de columna, más entradas se pueden acomodar en el conjunto de trabajo de la base de datos y los índices encajarán mejor en la memoria. Para este propósito, debe aprovechar los tipos específicos de la base de datos (por ejemplo, 

inet
para las direcciones IPv4 en PostgreSQL), especialmente porque Hibernate es muy flexible cuando se trata de implementar un nuevo tipo personalizado .

7. Relaciones

Hibernate viene con muchos tipos de mapeo de relaciones, pero no todos son iguales en términos de eficiencia.

Relaciones

@ManyToMany
Deben evitarse las colecciones y listas unidireccionales . Si realmente necesita utilizar colecciones de entidades, 
@OneToMany
se prefieren las asociaciones bidireccionales . Para la 
@ManyToMany
relación, use Set (s) ya que son más eficientes en este caso o simplemente mapee la tabla de muchos a muchos vinculada también y convierta la 
@ManyToMany
relación en dos 
@OneToMany
asociaciones bidireccionales .

Sin embargo, a diferencia de las consultas, las colecciones son menos flexibles ya que no se pueden paginar fácilmente, lo que significa que no podemos usarlas cuando el número de asociaciones secundarias es bastante alto. Por esta razón, siempre debes preguntarte si una colección es realmente necesaria. Una consulta de entidad puede ser una mejor alternativa en muchas situaciones.

8. Herencia

Cuando se trata de herencia, el desajuste de impedancia entre los lenguajes orientados a objetos y las bases de datos relacionales se vuelve aún más evidente. JPA ofrece 

SINGLE_TABLE
JOINED
TABLE_PER_CLASS
para lidiar con el mapeo de herencia, y cada una de estas estrategias tiene ventajas y desventajas.

SINGLE_TABLE
rinde mejor en términos de declaraciones SQL , pero perdemos en el lado de la integridad de los datos, ya que no podemos usar 
NOT NULL
restricciones.

JOINED
aborda la limitación de la integridad de los datos mientras ofrece declaraciones más complejas. Siempre que no use consultas o 
@OneToMany
asociaciones polimórficas contra tipos base, esta estrategia está bien. Su verdadero poder proviene de 
@ManyToOne
asociaciones polimórficas respaldadas por un patrón de estrategia en el lado de la capa de acceso a datos.

TABLE_PER_CLASS
 debe evitarse ya que no rinde sentencias SQL eficientes.

9. Tamaño del contexto de persistencia

Al usar JPA e Hibernate, siempre debe tener en cuenta el tamaño del contexto de persistencia. Por esta razón, nunca debes inflarlo con toneladas de entidades administradas. Al restringir la cantidad de entidades administradas, obtenemos una mejor administración de la memoria y el mecanismo de verificación sucio predeterminado también será más eficiente.

10. Obtener solo lo necesario

Obtener demasiados datos es probablemente la causa número uno de los problemas de rendimiento de la capa de acceso a los datos. Un problema es que las consultas de entidad se utilizan exclusivamente, incluso para proyecciones de solo lectura.

Las proyecciones DTO son más adecuadas para obtener vistas personalizadas , mientras que las entidades solo deben buscarse cuando el flujo de negocios requiera modificarlas.

La búsqueda EAGER es la peor , y debe evitar anti-patrones como Open-Session in View .

11. Almacenamiento en caché

Capas de caché

Los sistemas de bases de datos relacionales utilizan muchas estructuras de búfer en memoria para evitar el acceso al disco . El almacenamiento en caché de la base de datos a menudo se pasa por alto . Podemos reducir significativamente el tiempo de respuesta ajustando adecuadamente el motor de la base de datos para que el conjunto de trabajo resida en la memoria y no se extraiga del disco todo el tiempo.

El almacenamiento en caché a nivel de aplicación no es opcional para muchas aplicaciones empresariales. El almacenamiento en caché a nivel de aplicación puede reducir el tiempo de respuesta al tiempo que ofrece un almacenamiento secundario de solo lectura para cuando la base de datos está inactiva por mantenimiento o debido a una falla grave del sistema.

La caché de segundo nivel es muy útil para reducir el tiempo de respuesta de las transacciones de lectura y escritura, especialmente en arquitecturas de replicación primaria única . Dependiendo de los requisitos de la aplicación, Hibernate le permite elegir entre READ_ONLY , NONSTRICT_READ_WRITE , READ_WRITE y TRANSACTIONAL .

12. Control de concurrencia

La elección del nivel de aislamiento de las transacciones es de suma importancia en lo que respecta al rendimiento y la integridad de los datos. Para los flujos web de múltiples solicitudes, para evitar la pérdida de actualizaciones , debe usar el bloqueo optimista con entidades separadas o un 

EXTENDED
contexto de persistencia .

Para evitar 

optimistic locking
falsos positivos, puede utilizar el control de simultaneidad optimista sin versión o dividir conjuntos de propiedades basados ​​en escritura basados ​​en entidades .

13. Dar rienda suelta a las capacidades de consulta de bases de datos

El hecho de que utilice JPA o Hibernate no significa que no deba utilizar consultas nativas. Usted debe tomar ventaja de funciones de la ventana , CTE (expresiones de tabla comunes), 

CONNECT BY
PIVOT
.

Estas construcciones le permiten evitar obtener demasiados datos solo para transformarlos más adelante en la capa de aplicación. Si puede dejar que la base de datos realice el procesamiento, puede obtener solo el resultado final y, por lo tanto, ahorrar una gran cantidad de E / S de disco y gastos generales de red. Para evitar sobrecargar el nodo principal, puede utilizar la replicación de la base de datos y tener varios nodos de réplica disponibles para que las tareas de uso intensivo de datos se ejecuten en una réplica en lugar de en la principal.

14. Escalar y escalar horizontalmente

Las bases de datos relacionales escalan muy bien. Si Facebook , Twitter , Pinterest o StackOverflow pueden escalar su sistema de base de datos, es muy probable que pueda escalar una aplicación empresarial a sus requisitos comerciales particulares.

Punto de integración de la base de datos

La replicación y fragmentación de bases de datos son muy buenas formas de aumentar el rendimiento, y debe aprovechar totalmente estos patrones arquitectónicos probados en batalla para escalar su aplicación empresarial.

Conclusión

Una capa de acceso a datos de alto rendimiento debe resonar con el sistema de base de datos subyacente. Conocer el funcionamiento interno de una base de datos relacional y los marcos de acceso a datos en uso puede marcar la diferencia entre una aplicación empresarial de alto rendimiento y una que apenas se arrastra.

Fuente: https://vladmihalcea.com/14-high-performance-java-persistence-tips/