multithreading oracle11g jms contention rowlocking

multithreading - Conflicto de fila oráculo que causa errores de interbloqueo en la aplicación JMS de alto rendimiento



oracle11g contention (2)

Resumen:

Estoy interesado en saber cuál es la mejor práctica para aplicaciones de alto rendimiento que tienen mensajes masivos que intentan actualizar la misma fila y obtener errores de interbloqueo de Oracle. Sé que no puedes evitar esos errores, pero ¿cómo te recuperas de ellos con elegancia sin empantanarse por errores de interbloqueo que ocurren una y otra vez?

Detalles:

Estamos construyendo una aplicación de mensajería JMS de alto rendimiento. El entorno de producción será de dos nodos wegóricos 11g (ejecutando 6 instancias de detector MDB cada uno). Obtenemos errores de interbloqueo de Oracle (ORA-00060) cuando recibimos alrededor de 1000 mensajes que intentan actualizar la misma fila en la base de datos de Oracle. La sincronización de Java entre nodos no es posible en la API estándar de subprocesos de Java (a menos que no haya otra solución, no queremos utilizar ninguna solución de terceros como terracota, etc.).

Esperábamos que la declaración de Oracle "seleccione para la actualización WAIT n secs" ayude porque básicamente hará que los hilos de la competencia (para la misma fila) esperen unos segundos antes de que el primer hilo (quien obtuvo el bloqueo en la fila primero) termine con esto .

El primer problema con "SELECT FOR UPDATE WAIT n" es que no permite el uso de milisegundos para los tiempos de espera. Esto comienza a afectar negativamente el rendimiento de nuestra aplicación porque poner 1 segundo WAIT (menos tiempo de espera) causa retrasos en los mensajes.

Lo segundo es que estamos jugando con el parámetro de retardo de retransmisión de la cola weblogic (30 segundos en nuestro caso). Cada vez que un hilo rebota debido al error de interbloqueo, esperará 30 segundos antes de volver a intentarlo.

En nuestra experiencia, 1000 mensajes que compiten entre sí, en muchas situaciones tardan una eternidad en procesarse porque el punto muerto sigue sucediendo una y otra vez.

Entiendo que con la arquitectura actual se supone que tenemos errores de interbloqueo independientemente (en el caso de 1000 mensajes que compiten) pero la aplicación debe ser lo suficientemente flexible como para recuperarse de estos errores después de volver a intentar los mensajes en bucle.

¿Alguna idea de lo que nos falta aquí? alguien que ha tratado con problemas similares antes?

Estoy buscando algunas ideas de diseño que puedan hacer que esto funcione de forma resiliente para que se recupere de esta situación de punto muerto y eventualmente procese todos los mensajes en un tiempo razonable sin usar mucho hardware adicional.

DETALLES DE LA COMPUTADORA: Estos 1000 mensajes crearán CADA UNO 4 objetos de 4 tipos de posiciones diferentes, cada uno con una cantidad asociada. Estas cantidades deberán fusionarse en esas 4 ranuras diferentes (dependiendo del tipo de posición). El punto muerto se produce cuando esas 4 ranuras individuales se actualizan por cada subproceso individual. Ya hemos ordenado esas actualizaciones individuales en un orden específico antes de aplicarlas a las filas de la base de datos para evitar posibles condiciones de carrera.


En cuanto al MDB

  1. Deje que consuma los mensajes y actualice las variables de instancia que contienen el delta de las cantidades de los mensajes procesados ​​(un MDB puede llevar el estado en sus variables de instancia a través de múltiples mensajes).

  2. Un método @Schedule en el mismo MDB persiste las cantidades en una única transacción de base de datos utilizando una única instrucción SQL por segundo (por ejemplo)

update x set q1 = q1 + delta1, q2 = q2 + delta2, ...

He hecho algunas pruebas:

  • Se necesitan 6 segundos para crear 1000 mensajes (JBoss 7 usando HornetQ)
  • Durante ese tiempo, 840 mensajes ya se conservaron.
  • Tarda otros 2 segundos en persistir los restantes (el método programado se ejecutó cada segundo)
  • Esto requirió siete comandos de actualización SQL en siete transiciones DB
  • La carga es completamente causada por la creación de los mensajes; no hay carga real en el DB

Notas

  • Necesita otro método @PreDestroy para conservar los deltas pendientes y asegurarse de que no se pierda nada
  • Si debe garantizar la corrección transaccional, este enfoque no es adecuado. En ese caso, sugiero usar un receptor de cola normal (= sin MDB), sesión transaccionada y receive(timeout) para recolectar de 100 a 10000 mensajes (o hasta un tiempo de espera), hacer una transacción de base de datos y luego la confirmación en la cola sesión. Esto es mejor, pero todavía no es XA transaccional. Si necesita esto, ambas confirmaciones deben coordinarse mediante una única transacción XA.

Un punto muerto implica que cada subproceso intenta actualizar varias filas en una sola transacción y que esas actualizaciones se realizan en un orden diferente entre subprocesos. La respuesta más simple posible, por lo tanto, sería modificar el código para que los mensajes dentro de la misma transacción se apliquen en un orden definido (es decir, en orden de la clave principal). Eso garantizaría que nunca llegarías a un punto muerto, aunque seguirías bloqueando bloqueos mientras un hilo espera a que otro hilo realice su transacción.

Sin embargo, dando un paso atrás, parece poco probable que realmente desee que muchos hilos actualicen la misma fila en una tabla cuando no puede predecir el orden de las actualizaciones. Parece muy probable que conduzca a una gran cantidad de actualizaciones perdidas y un comportamiento bastante impredecible. ¿Qué hace exactamente tu aplicación que haga que este tipo de cosas sean sensatas? ¿Está haciendo algo así como actualizar tablas agregadas después de insertar filas en una tabla de detalles (es decir, actualizar el recuento de la cantidad de vistas que tiene una publicación además de registrar información sobre una vista particular)? Si es así, ¿esas operaciones realmente necesitan ser sincrónicas? ¿O podría actualizar el recuento de vistas periódicamente en otro subproceso al agregar las vistas en el último N segundo?