Cómo está Hibernate decidiendo el orden de actualización/inserción/eliminación
deadlock database-deadlocks (2)
Primero olvidemos Hibernate. Supongamos que tengo dos tablas, A y B. Dos transacciones están actualizando los mismos registros en estas dos tablas, pero txn 1 actualiza B y luego A, mientras que txn 2 actualiza A y luego B. Este es un típico ejemplo de interbloqueo. La forma más común de evitar esto es predefinir el orden de adquisición de recursos. Por ejemplo, debemos actualizar la tabla A y luego B.
Regresa a Hibernate. Cuando estamos actualizando muchas entidades en una sesión, una vez que estoy descargando la sesión, los cambios de las diferentes entidades generarán las correspondientes instrucciones de inserción / actualización / eliminación en la base de datos. ¿Hibernate tiene algún algoritmo para decidir el orden de actualización entre las entidades? De lo contrario, ¿cuál es la forma en que Hibernate utilizó para prevenir la situación de estancamiento descrita en el 1er párrafo?
Si Hibernate mantiene el orden, ¿cómo puedo saber o controlar el pedido? No quiero que mi actualización explícita en conflictos de DB con Hibernate, y causar un punto muerto.
Con respecto al primer ejemplo, este tipo de cosas es manejado por la base de datos (lea más sobre los niveles de aislamiento de transacciones y las estrategias de bloqueo de su base de datos). Hay muchas formas diferentes de manejar esto.
En cuanto a Hibernate, javadoc a org.hibernate.event.def.AbstractFlushingEventListener.performExecutions (EventSource) dice:
Ejecute todas las actualizaciones de caché de SQL y de segundo nivel, en un orden especial para que no se puedan violar las restricciones de clave externa:
- Inserciones, en el orden en que fueron realizadas
- Actualizaciones Eliminación de elementos de colección
- Inserción de elementos de colección
- Elimina, en el orden en que se realizaron
Supongo que esta es la única optimización de las consultas SQL ejecutadas que hace Hibernate. El resto de los problemas es manejado por la base de datos.
El problema que describes no es manejado por la base de datos, y según mi experiencia, Hibernate tampoco lo maneja del todo.
Tienes que tomar medidas explícitas para evitar que sea un problema.
Hibernate hace parte del trabajo por usted. Según la respuesta anterior, Hibernate garantiza que, dentro de una descarga aislada, las inserciones, eliminaciones y actualizaciones se ordenan de forma que garantice que se aplicarán en un orden alcanzable. Consulte performExecutions (sesión de EventSource) en la clase AbstractFlushingEventListener:
Ejecute todas las actualizaciones de SQL (y de caché de segundo nivel) en un orden especial para que las restricciones de clave externa no puedan ser violadas:
- Inserciones, en el orden en que fueron realizadas
- Actualizaciones
- Eliminación de elementos de colección
- Inserción de elementos de colección
- Elimina, en el orden en que se realizaron
Cuando tenga restricciones únicas, es muy importante conocer este orden, especialmente si desea reemplazar un elemento secundario por uno (eliminar anterior / insertar nuevo), pero tanto el antiguo como el nuevo comparten las mismas restricciones únicas (por ejemplo, la misma dirección de correo electrónico) ) En este caso, puede actualizar la entrada anterior, en lugar de eliminar / insertar, o puede enjuagar después de eliminar solo para luego continuar la inserción. Para un ejemplo más detallado, puedes consultar este artículo .
Tenga en cuenta que no especifica el orden de las actualizaciones. El examen del código de Hibernate me lleva a pensar que el orden de actualización dependerá del orden en que las entidades se agregaron al contexto de persistencia, NO del orden en que se actualizaron. Eso podría ser predecible en su código, pero leer el código de Hibernate no me hizo sentir que confiaría en ese orden.
Hay tres soluciones que puedo pensar:
- Intente configurar hibernate.order_updates para que sea verdadero . Esto debería ayudar a evitar interbloqueos cuando se actualizan varias filas en la misma tabla, pero no ayudará con los interbloqueos en múltiples tablas.
- Haga que sus transacciones tomen un bloqueo PESSIMISTIC_WRITE en una de las entidades antes de realizar cualquier actualización. La entidad que utilice dependerá de su situación específica, pero siempre que se asegure que una entidad se elija de forma consistente si existe un riesgo de estancamiento, esto bloqueará el resto de la transacción hasta que se pueda obtener el bloqueo.
- Escriba su código para atrapar los bloqueos cuando ocurran y vuelva a intentarlo de manera sensata. El componente que gestiona el reintento de bloqueo muerto debe ubicarse fuera del límite de transacción actual. Esto se debe a que la sesión anómala debe cerrarse y la transacción asociada debe respaldarse. En este artículo , puede encontrar un ejemplo de un AOP de reintentos automático.