2011-02-02 8 views
9

Quiero preguntar cuál podría ser la mejor solución para la aplicación Java multiproceso para garantizar que todos los subprocesos accedan db de forma síncrona. Por ejemplo, cada hilo representa una transacción separada, y primero verifica el valor de db y luego, dependiendo de la respuesta, tiene que insertar o actualizar algunos campos en la base de datos (nota entre verificación, inserción y aplicación de confirmación está haciendo otros procesamientos). Pero el problema es que otro hilo podría estar haciendo exactamente lo mismo en la misma mesa. Ejemplo más específico. El subproceso T1 inicia la transacción, luego verifica la tabla ENTITY_TABLE para ingresar con el código '111' si se encuentra la fecha de actualización, si no se encuentra, inserta una nueva entrada y luego confirma la transacción. Ahora imagine que el hilo T2 hace exactamente lo mismo. Ahora hay algunos problemas: 1. T1 y T2 comprueban db y no encuentran nada y ambos insertan la misma entrada. 2. T1 comprueba db, encuentra la entrada con una fecha anterior, pero en la confirmación T2 ya tiene la entrada actualizada en una fecha más reciente. 3. Si usamos la memoria caché y sincronizamos el acceso a la memoria caché, tenemos un problema: T1 adquiere las comprobaciones de bloqueo db y la memoria caché si no se encuentra agrega a la memoria caché, suelta el bloqueo, confirma. T2 hace lo mismo, encuentra que la entrada en el caché va a comprometerse. Pero la transacción T1 falla y está respaldada. Ahora T2 está en mal estado, porque debería insertarse en ENTITY_TABLE pero no lo sabe. 4. más?Acceso a la base de datos de subprocesos múltiples de Java

Estoy trabajando en la creación de memoria caché personalizada simple con la sincronización y solución de problemas 3. Pero estoy interesado tal vez hay una solución más simple? ¿Alguien tuvo que resolver un problema similar? ¿Como lo hiciste?

Respuesta

7

Esto se debe tratar principalmente dentro de la base de datos, configurando el nivel transaction isolation deseado. Luego, además de esto, debe seleccionar su estrategia de bloqueo (optimistic o pesimista).

Sin aislamiento de transacción, tendrá dificultades para tratar de garantizar la integridad de la transacción únicamente en el dominio de Java. Especialmente teniendo en cuenta que incluso si actualmente solo se accede a la base de datos desde su aplicación Java, esto puede cambiar en el futuro.

Ahora, en cuanto a qué nivel de aislamiento elegir, a partir de su descripción podría parecer que necesita el mayor nivel de aislamiento, serializable. Sin embargo, en la práctica esto tiende a para ser un cerdo de rendimiento real debido a un bloqueo extensivo. Por lo tanto, es posible que desee reevaluar sus requisitos para encontrar el mejor equilibrio de aislamiento y rendimiento para su situación específica.

+0

Gracias por una respuesta. La cuestión es que la aplicación debe ser muy eficiente, puede ejecutarse en 16 o incluso 32 subprocesos. Y cada transacción comienza, procesa mucho, luego toma resultados y realiza inserciones y actualizaciones en el paso de confirmación, todo en lotes jdbc (para rendimiento). Entonces la transacción está comprometida. De todos modos, en mi situación, el nivel serializable sería demasiado, especialmente para 32 hilos. Espero ser capaz de resolver esto con caché en la aplicación Java. – nesvarbu

1

Offhand, creo que tendrías que bloquear la mesa antes de consultarla. Esto forzará el funcionamiento secuencial de tus hilos. Sus hilos deberían estar preparados para el hecho de que tendrán que esperar el bloqueo y, por supuesto, la adquisición de bloqueo podría expirar. Esto podría introducir un cuello de botella en su aplicación, así como todos sus hilos tendrán que hacer cola para obtener recursos de la base de datos.

1

El problema al que se enfrenta es transaction isolation.

Parece que necesita que cada hilo bloquee la fila correspondiente en la cláusula where, que requiere aislamiento serializable.

1

¿Por qué estás tratando de reinventar la rueda? Sugiero usar un marco de correlación OR para acceder a la base de datos con transacciones (por ejemplo, un implementador de especificaciones JPA como Hibernate o Eclipselink). También puede agregar Spring DAO, que maneja las transacciones por usted. Entonces puedes concentrarte en la lógica de negocios y no tienes que molestarte con esas cosas de bajo nivel.

2

Si desea SQL SELECCIONE una fila de una base de datos, y luego ACTUALICE la misma fila, tiene 2 opciones como desarrollador de Java.

  1. SELECT con ROWLOCK, o lo que sea la sintaxis de bloqueo fila es para su base de datos en particular .

  2. seleccione la fila, hacer su procesamiento, y justo antes de que esté listo para actualización, seleccione la fila de nuevo para ver si cualquier otro hilo hizo cambios. Si los dos SELECTS devuelven los mismos valores , ACTUALIZA. De lo contrario, ejecute un error o vuelva a procesar.

2

Me topé con este problema cuando trabajaba con un programa de Java multiproceso que utilizaba una base de datos Sqllite. Utiliza el bloqueo de archivos, así que tuve que asegurarme de que solo un hilo estaba trabajando al mismo tiempo.

Básicamente terminé usando sincronización. Cuando ConnectionFactory devuelve una conexión db, también devuelve un objeto de bloqueo que se debe bloquear cuando se utiliza la conexión. Por lo que podría hacer bloqueo de sincronización manualmente, o crear una subclase de la clase por debajo del cual lo hace por usted:

/** 
    * Subclass this class and implement the persistInTransaction method to perform 
    * an update to the database. 
    */ 
public abstract class DBOperationInTransaction { 

    protected Logger logger = Logger.getLogger(DBOperationInTransaction.class.getName()); 

    public DBOperationInTransaction(ConnectionFactory connectionFactory) { 
     DBConnection con = null; 

     try { 
      con = connectionFactory.getConnection(); 

      if(con == null) { 
       logger.log(Level.SEVERE, "Could not get db connection"); 
       throw new RuntimException("Could not get db connection"); 
      } 

      synchronized (con.activityLock) { 
       con.connection.setAutoCommit(false); 
       persistInTransaction(con.connection); 
       con.connection.commit(); 
      } 

     } catch (Exception e) { 
      logger.log(Level.SEVERE, "Failed to persist data:", e); 
      throw new RuntimeException(e); 
     } finally { 
      if(con != null) { 
       //Close con.connection silently. 
      } 
     } 
    } 

    /** 
     * Method for persisting data within a transaction. If any SQLExceptions 
     * occur they are logged and the transaction is rolled back. 
     * 
     * In the scope of the method there is a logger object available that any 
     * errors/warnings besides sqlException that you want to log. 
     * 
     * @param con 
     *   Connection ready for use, do not do any transaction handling 
     *   on this object. 
     * @throws SQLException 
     *    Any SQL exception that your code might throw. These errors 
     *    are logged. Any exception will rollback the transaction. 
     * 
     */ 
    abstract protected void persistInTransaction(Connection con) throws SQLException; 

} 

Y la estructura DBConnection:

final public class DBConnection { 
    public final Connection connection; 
    public final String activityLock; 

    public DBConnection(Connection connection, String activityLock) { 
     this.connection = connection; 
     this.activityLock = activityLock; 
    } 

} 
Cuestiones relacionadas