2011-01-13 13 views
6

Estoy teniendo problemas con SELECT de MySQL .. FOR UPDATE, aquí es la consulta que estoy tratando de ejecutar:JDBC bloqueo de una fila usando SELECT FOR UPDATE no funciona

SELECT * FROM tableName WHERE HostName='UnknownHost' 
     ORDER BY UpdateTimestamp asc limit 1 FOR UPDATE 

Después de esto, la preocupación thread hará una ACTUALIZACIÓN y cambiará el HostName, que es entonces debería desbloquear la fila.

estoy corriendo una aplicación multi-hilo de Java, de modo 3 hilos están ejecutando esta sentencia SQL, pero cuando se ejecuta este hilo 1, no bloquee sus resultados a partir de hilos 2 & 3. Por lo tanto hilos 2 & 3 están recibiendo los mismos resultados y podrían actualizar la misma fila.

Además, cada hilo tiene su propia conexión mysql.

estoy usando InnoDB, la transacción de aislamiento READ-= cometidos y el compromiso automático está desactivado antes de ejecutar la actualización de selección para

puedo perder algo? O tal vez hay una mejor solución? Muchas gracias.

Código:

public BasicJDBCDemo() 
{ 
    Le_Thread newThread1=new Le_Thread(); 
    Le_Thread newThread2=new Le_Thread(); 
    newThread1.start(); 
    newThread2.start();   
} 

Tema:

class Le_Thread extends Thread 
{ 

    public void run() 
    { 
    tring name = Thread.currentThread().getName(); 
     System.out.println(name+": Debut."); 
    long oid=Util.doSelectLockTest(name); 
    Util.doUpdateTest(oid,name);   
    } 

} 

Seleccionar:

public static long doSelectLockTest(String threadName) 
    { 
    System.out.println("[OUTPUT FROM SELECT Lock ]...threadName="+threadName); 
    PreparedStatement pst = null; 
    ResultSet rs=null; 
    Connection conn=null; 
    long oid=0; 
    try 
    { 
    String query = "SELECT * FROM table WHERE Host=? 
           ORDER BY Timestamp asc limit 1 FOR UPDATE"; 


     conn=getNewConnection(); 
     pst = conn.prepareStatement(query); 
     pst.setString(1, DbProperties.UnknownHost); 
     System.out.println("pst="+threadName+"__"+pst); 
     rs = pst.executeQuery(); 

     if (rs.first()) 
     { 
     String s = rs.getString("HostName"); 
     oid = rs.getLong("OID"); 
     System.out.println("oid_oldest/host/threadName=="+oid+"/"+s+"/"+threadName); 

     } 

    } 
    catch (SQLException ex) 
    { 
     ex.printStackTrace(); 
    } 
    finally 
    { 
     DBUtil.close(pst); 
     DBUtil.close(rs); 
     DBUtil.close(conn); 
    } 
    return oid; 
    } 

Por favor, ayuda ....:

Resultado:

 
Thread-1: Debut. 
Thread-2: Debut. 
[OUTPUT FROM SELECT Lock ]...threadName=Thread-1 
New connection.. 
[OUTPUT FROM SELECT Lock ]...threadName=Thread-2 
New connection.. 
pst=Thread-2: SELECT * FROM b2biCheckPoint WHERE HostName='UnknownHost' ORDER BY UpdateTimestamp asc limit 1 FOR UPDATE 
pst=Thread-1: SELECT * FROM b2biCheckPoint WHERE HostName='UnknownHost' ORDER BY UpdateTimestamp asc limit 1 FOR UPDATE 
oid_oldest/host/threadName==1/UnknownHost/Thread-2 
oid_oldest/host/threadName==1/UnknownHost/Thread-1 
[Performing UPDATE] ... oid = 1, thread=Thread-2 
New connection.. 
[Performing UPDATE] ... oid = 1, thread=Thread-1 
pst_threadname=Thread-2: UPDATE b2bicheckpoint SET HostName='1_host_Thread-2',UpdateTimestamp=1294940161838 where OID = 1 
New connection.. 
pst_threadname=Thread-1: UPDATE b2bicheckpoint SET HostName='1_host_Thread-1',UpdateTimestamp=1294940161853 where OID = 1 
+0

¿Cómo está logrando este código sabio? –

+0

¿Qué quieres decir con eso? – Rachid

Respuesta

1

La conexión que cree que selecciona para la actualización debe ser la misma que se utiliza para realizar la actualización. De lo contrario, no es parte de la misma transacción y libera el bloqueo, para que sus otros hilos comiencen a ejecutarlo también. Así que en el código Se necesita hacer esto:

if (rs.first()) 
    { 
    String s = rs.getString("HostName"); 
    oid = rs.getLong("OID"); 
    System.out.println("oid_oldest/host/threadName=="+oid+"/"+s+"/"+threadName); 

    } 
Util.doUpdateTest(oid,name,conn); 
conn.commit(); 
+0

Hola Hiro2k, gracias por tu respuesta. No puedo cambiar la configuración del servidor de la base de datos, es decir, no puedo cambiar el archivo my.ini. Pero en el manual de MySQL dicen que si se establece READ-COMMITTED, solo se bloqueará el índice, lo que está bien en mi caso porque la columna HostName es un índice ... – Rachid

+0

¿Podría enviarme su código de ejemplo y luego? Lo comparo con el mío? – Rachid

+0

Hola Rachid, no puedo compartir el código, pero acabo de recordar que puedes cambiar el nivel de aislamiento en la conexión usando setTransactionIsolation. Pruébalo y mira si funciona para ti. – Hiro2k

0

Ok, aquí está mi nuevo método selectLockTest:

código:

public static long doSelectLockTest(String threadName) { 
    System.out.println("[OUTPUT FROM SELECT Lock ]...threadName=" + threadName); 
    PreparedStatement pst = null; 
    ResultSet rs = null; 
    Connection conn = null; 
    long oid = 0; 
    try { 
     String query = "SELECT * FROM table WHERE Host=? ORDER BY UpdateTime asc limit 1 FOR UPDATE"; 

     conn = getNewConnection(); 
     conn.setAutoCommit(false); 
     pst = conn.prepareStatement(query); 

     pst.setString(1, DbProperties.UnknownHost); 
     rs = pst.executeQuery(); 

     if (rs.first()) { 
      String s = rs.getString("HostName"); 
      oid = rs.getLong("OID"); 

      //Start update then commit 
      if (oid != 0) { 

       query = "UPDATE b2bicheckpoint SET HostName=?,UpdateTimestamp=? where OID = ?"; 

       pst = conn.prepareStatement(query); 
       pst.setString(1, oid + "_host_" + threadName); 
       pst.setLong(2, getCurrentLongTime()); 
       pst.setLong(3, oid); 
       System.out.println("Select_Prestatement=" + threadName + "__" + pst); 

       int result = pst.executeUpdate(); 
       conn.commit(); 
       conn.setAutoCommit(true); 
      } 

     } 

    } catch (SQLException ex) { 
     ex.printStackTrace(); 
    } finally { 
     DBUtil.close(pst); 
     DBUtil.close(rs); 
     DBUtil.close(conn); 
    } 
    return oid; 
} 

Resultado:

[OUTPUT FROM SELECT Lock ]...threadName=Thread-1 
[OUTPUT FROM SELECT Lock ]...threadName=Thread-2 
Select_Prestatement=Thread-1_ : SELECT * FROM ..... FOR UPDATE 
Select_Prestatement=Thread-2_: SELECT * FROM ...... FOR UPDATE 
Select_Prestatement=Thread-1_: UPDATE table SET HostName='host_Thread-1' where OID = 1 

Significa dos SELECT FOR UPDATE se ejecutó (uno por hilo) pero solo se ejecutó una actualización y el host thread_1 se mantuvo. Es mejor, pero es el comportamiento esperado?

Gracias

+1

no debe hacer preguntas adicionales en forma de respuesta. –

8

Usted son super confuso, pero al menos las cosas se ven mejor después de sus ediciones.Hay varias maneras de hacer esto, pero la mejor manera que he encontrado es utilizar realmente ResultSet.update* métodos de JDBC:

En primer lugar, es necesario preparar su declaración SELECT ... FOR UPDATE con el argumento ResultSet.CONCUR_UPDATABLE, así:

ps = conn.prepareStatement(query, 
          ResultSet.TYPE_FORWARD_ONLY, 
          ResultSet.CONCUR_UPDATABLE); 

Entonces, usted tiene que actualizar la tabla de hecho usando el conjunto de resultados:

if(rs.next()) 
{ 
    rs.updateString(columnIndex, "new_hostname"); 
    rs.updateRow(); 
} 

en tercer lugar, es probable que necesite usar una transacción, lo que puedo ver en su actualización. Afortunadamente, sus métodos DbUtil.close no arrojarán ninguna excepción, verificarán nulos, etc. Además, si su método se vuelve más complicado, también debería tener una lógica de reversión.

No debería tener que modificar my.ini por ningún motivo.

Cuestiones relacionadas