2008-09-24 17 views
25

Tengo una aplicación, más parecida a una utilidad, que se encuentra en una esquina y actualiza periódicamente dos bases de datos diferentes.¿Cuál es la "mejor" forma de hacer transacciones distribuidas en múltiples bases de datos usando Spring and Hibernate

Es una pequeña aplicación independiente que se ha creado con un contexto de aplicación de primavera. El contexto tiene dos Hibernate Session Factories configuradas en él, a su vez utilizando fuentes de datos de Commons DBCP configuradas en Spring.

Actualmente no hay administración de transacciones, pero me gustaría agregar algunas. La actualización de una base de datos depende de una actualización exitosa de la otra.

La aplicación no se encuentra en un contenedor Java EE, sino que está iniciada por una clase de iniciador estático llamada desde un script de shell. La clase de iniciador crea una instancia del contexto de la aplicación y luego invoca un método en uno de sus beans.

¿Cuál es la "mejor" forma de poner la transaccionalidad alrededor de las actualizaciones de la base de datos?

Dejaré la definición de "mejor" para ti, pero creo que debería ser una función de "fácil de configurar", "fácil de configurar", "barata" y "fácil de empaquetar y redistribuir" . Naturalmente, FOSS sería bueno.

Respuesta

26

La mejor forma de distribuir transacciones en más de una base de datos es: no.

Algunas personas lo señalarán con XA pero XA (o Comando de dos fases) es una mentira (o marketese).

Imagínese: después de la primera fase le dijo al administrador de XA que puede enviar la confirmación final, la conexión de red a una de las bases de datos falla. ¿Ahora que? ¿Se acabó el tiempo? Eso dejaría la otra base de datos corrupta. ¿Retroceder? Dos problemas: no puede deshacer una confirmación y ¿cómo sabe qué sucedió con la segunda base de datos? ¿Tal vez la conexión de red falló después de que haya confirmado los datos y solo se haya perdido el mensaje de "éxito"?

La mejor manera es copiar los datos en un solo lugar. Utilice un esquema que le permita abortar la copia y continuarla en cualquier momento (por ejemplo, ignore los datos que ya tiene o solicite el ID seleccionado por ID y solicite solo registros> MAX (ID) de su copia). Protege esto con una transacción. Esto no es un problema, ya que solo está leyendo datos de la fuente, por lo que cuando la transacción falla por algún motivo, puede ignorar la base de datos de origen. Por lo tanto, esta es una simple transacción de origen única.

Después de haber copiado los datos, trátelos localmente.

+5

Las transacciones distribuidas deben registrar las 4 propiedades ACID. ¿Cuál es tu problema? El escenario que describió no puede suceder, ya que los gerentes se comunican entre sí y solo se comprometen cuando todos los nodos participantes han intercambiado un "IR". – Falcon

+6

@Falcon: ¿Qué ocurre si la red falla entre PREPARE y COMMIT? O uno de los servidores muere? "no puede suceder" no puede suceder en la realidad. –

+2

No, no se les ordena retroceder porque en este escenario, algunos de los nodos ya se han confirmado. Lo que sucede es que cuando el nodo bloqueado está disponible, el coordinador de transacciones le dice que vuelva a comprometerse. Debido a que el nodo respondió positivamente en la fase de "preparación", se requiere poder "comprometer", incluso cuando se recupera de un bloqueo. –

6

Configure un administrador de transacciones en su contexto. Los documentos de Spring tienen ejemplos, y es muy simple. Luego, cuando se desea ejecutar una transacción:

try { 
    TransactionTemplate tt = new TransactionTemplate(txManager); 

    tt.execute(new TransactionCallbackWithoutResult(){ 
    protected void doInTransactionWithoutResult(
      TransactionStatus status) { 
     updateDb1(); 
     updateDb2(); 
    } 
} catch (TransactionException ex) { 
    // handle 
} 

Para más ejemplos, y la información tal vez mirar esto: XA transactions using Spring

+3

Este ejemplo realmente no responde a la pregunta o incluso la responde incorrectamente: el OP mencionó que tenía dos fábricas de sesión de Hibernate configuradas que requerirían dos administradores de transacciones por separado. El ejemplo en la respuesta solo usa un administrador de transacciones que no se especifica más de cerca. El uso de un único gestor de transacciones de Hibernate nunca anularía uno de los dos DB en caso de error. Usar, por ejemplo, un 'ChainedTransactionManager' (como notó @Pani Dhakshnamurthy) podría ayudar, pero eso no se menciona en esta respuesta. – Chriki

5

Cuando dice "dos bases de datos diferentes", ¿quiere decir diferentes servidores de bases de datos, o dos esquemas diferentes dentro del mismo servidor de base de datos?

Si es el primero, entonces si desea la transaccionalidad completa, entonces necesita la API de transacción XA, que proporciona la confirmación de dos fases completa. Pero lo más importante es que también necesita un coordinador/monitor de transacciones que administre la propagación de transacciones entre los diferentes sistemas de bases de datos. Esto es parte de la especificación JavaEE, y una parte bastante enrarecida de eso. El coordinador de TX en sí es una pieza compleja de software. El software de su aplicación (a través de Spring, si lo desea) habla con el coordinador.

Sin embargo, si solo quiere decir dos bases de datos dentro del mismo servidor de bases de datos, entonces las transacciones JDBC de vainilla deberían funcionar bien, solo realice sus operaciones con ambas bases de datos en una sola transacción.

3

En este caso necesitaría un monitor de transacciones (servidor compatible con el protocolo XA) y asegúrese de que sus bases de datos también sean compatibles con XA. La mayoría (¿todos?) De los servidores J2EE vienen con el Monitor de Transacción incorporado. Si su código no se ejecuta en el servidor J2EE, existen muchas alternativas independientes: Atomicos, Bitronix, etc.

Cuestiones relacionadas