java - false - transacciones spring hibernate
¿Por qué las transacciones de base de datos de solo lectura de Spring/Hibernate se ejecutan más lentamente que las de lectura-escritura? (1)
He estado investigando sobre el rendimiento de las transacciones de base de datos de solo lectura frente a las de lectura y escritura. El servidor MySQL es remoto, por lo que es fácil para mí ver las diferencias entre los diferentes tipos de transacciones. Esto es con la agrupación de conexiones que sé que funciona según la comparación de las llamadas JDBC 1a versus 2a.
Cuando configuro el Spring AOP para usar una transacción de solo lectura en mi llamada DAO, las llamadas son un 30-40% más lentas en comparación con las de lectura-escritura:
<!-- slower -->
<tx:method name="find*" read-only="true" propagation="REQUIRED" />
o
// slower
@Transaction(readOnly = true)
Versus:
<!-- faster -->
<tx:method name="find*" read-only="false" propagation="REQUIRED" />
o
// faster
@Transaction
Mirando a tcpdump, parece que la transacción de solo lectura está haciendo más y más al hablar con MySQL. Aquí está el volcado de solo read-write versus read-write .
¿Alguien puede explicar por qué las llamadas de solo lectura se están demorando? ¿Se espera esto?
¿Hay algo que estoy haciendo mal o algo que pueda hacer para mejorar su velocidad además de mejorar la red? Acabo de encontrar esta increíble publicación con algunas buenas recomendaciones de rendimiento . ¿Algún otro comentario?
Muchas gracias.
¿Por qué las transacciones de base de datos de solo lectura de Spring / Hibernate se ejecutan más lentamente que las de lectura-escritura?
Ok, este ha sido un paseo interesante. Mucho para que yo aprenda y comparta. Algunos de los siguientes puntos deberían haber sido obvios, pero espero que mi ignorancia y lo que he aprendido sean útiles para otros.
<tldr> La respuesta corta a la pregunta # 1 fue que hibernate inicia una @Transaction(readOnly = true)
con un set session.transaction.read.only
sincrónica llamada JDBC y termina con un set session.transaction.read.write
call . Estas llamadas no se envían cuando se realizan llamadas de lectura y escritura, por lo que las llamadas de solo lectura fueron más lentas. </tldr>
La respuesta más larga a la pregunta # 2 incluye los siguientes detalles de los pasos que tomé para intentar reducir el rendimiento de nuestra base de datos remota:
Lo primero que hicimos fue cambiar nuestra base de datos VPN de TCP a UDP después de leer esta página de optimización de OpenVPN . Suspiro. Debería haber sabido de esto. También agregué las siguientes configuraciones a las configuraciones de servidor y cliente OpenVPN. La sobrecarga de la transacción de solo lectura se redujo de 480 ms a 141 ms, pero fue aún más que los 100 ms de lectura y escritura. Gran victoria.
; Got these from: https://community.openvpn.net/openvpn/wiki/Gigabit_Networks_Linux proto udp tun-mtu 6000 fragment 0 mssfix 0
Al observar de cerca la salida de tcpdump (
tcpdump ... -X
para la ganancia), noté que se hacían muchas llamadas innecesarias de confirmación automática y de solo lectura / escritura JDBC. La actualización a una versión más reciente de la increíble biblioteca de grupos de conexión HikariCP que usamos nos ayudó con esto. En la versión 2.4.1 agregaron algo de inteligencia que redujo algunas de estas llamadas. Gastos generales de transacción de solo lectura hasta 120ms. Lectura-escritura aún a 100ms. Bonito.Brett Wooldridge, el autor de HikariCP me indicó la configuración del controlador MySQL que podría ayudar. Muchas gracias tio Al agregar la siguiente configuración a nuestra URL de JDBC de MySQL, se le indica al controlador que use el estado del software de la conexión y no le pregunte al servidor por el estado.
jdbc:mysql://.../database?useLocalSessionState=true&useLocalTransactionState=true
Estas configuraciones hicieron que se eliminaran más comandos JDBC síncronos. La sobrecarga de la transacción de solo lectura se redujo a 60 ms y ahora es lo mismo que lectura-escritura. Woo hoo
Editar / ADVERTENCIA: en realidad
useLocalTransactionState=true
agregandouseLocalTransactionState=true
después de que se encontraron errores donde el controlador no estaba enviando información de la transacción.Pero al mirar más a la salida de tcpdump, aún vi que se enviaba la configuración de transacción de solo lectura / lectura-escritura. Mi última solución fue escribir un grupo de detección de solo lectura personalizado que proporcione conexiones de un grupo especial si ve que la primera llamada a la conexión es
connection.setReadOnly(true)
.El uso de este grupo personalizado redujo la sobrecarga de la transacción para las conexiones de solo lectura y de lectura y escritura a 20 ms. Creo que básicamente eliminó la última de las llamadas de sobrecarga de transacción JDBC. Aquí está la fuente de las dos clases que escribí desde mi página de inicio y escribo todo esto. El código es relativamente frágil y se basa en que Hibernate hace una
connection.setReadOnly(true)
primera vez, pero parece estar funcionando bien y lo documenté cuidadosamente en el código XML y el código.
Por lo tanto, la sobrecarga básica de @Transaction
pasó de 480 ms a 20 ms durante un par de días de trabajo. 100 llamadas de hibernación "en la vida real" a un dao.find(...)
comenzaron a los 55 segundos y terminaron a los 4,5 segundos. Bastante patada de culo. Ojalá siempre fuera tan fácil conseguir una mejora de velocidad 10x.
Espero que mi experiencia ayude a otros.