2011-01-13 18 views
6

Ho dei problemi con SELECT di MySQL .. FOR UPDATE, qui è la domanda che sto cercando di eseguire:JDBC Blocco di fila usando SELECT FOR UPDATE, non funziona

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

Dopo questa, l'interessato filo farà un UPDATE e cambiare il nome host, allora dovrebbe sbloccare la riga.

Sto facendo funzionare un'applicazione multi-threaded Java, quindi 3 le discussioni sono in esecuzione questa istruzione SQL, ma quando il filo 1 viene eseguito questo, non blocca i suoi risultati a partire da fili 2 & 3. Pertanto le discussioni 2 & 3 sono sempre gli stessi risultati e potrebbero aggiornare la stessa riga.

Inoltre, ogni filetto inizia una propria connessione mysql.

sto usando InnoDB, con la transazione isolamento = read-committed, e l'Autocommit è spento prima di eseguire la selezione per aggiornamento

posso perdere qualcosa? O forse c'è una soluzione migliore? Grazie mille.

Codice:

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

Discussione:

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);   
    } 

} 

Seleziona:

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; 
    } 

Aiutateci ....:

Risultato:

 
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

Come stai ottenendo questo codice saggio? –

+0

Cosa intendi con questo? – Rachid

risposta

1

La connessione creata che si seleziona per l'aggiornamento deve essere la stessa utilizzata per eseguire l'aggiornamento. In caso contrario, non fa parte della stessa transazione e rilascia il blocco, in modo che le altri thread iniziano a eseguirlo pure. Quindi, nel codice, è necessario fare questo:

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

Ciao Hiro2k, grazie per la tua risposta. Non riesco a modificare la configurazione del server del database, voglio dire, non posso modificare il file my.ini. Ma nel manuale di MySQL dicono che se è stato impostato READ-COMMITTED, sarà bloccato solo l'indice, il che è ok nel mio caso perché la colonna HostName è un indice .... – Rachid

+0

Potresti inviarmi il tuo codice di esempio quindi Lo confronto con il mio? – Rachid

+0

Ciao Rachid, non posso condividere il codice ma mi sono appena ricordato che puoi cambiare il livello di isolamento sulla connessione usando setTransactionIsolation. Fare un tentativo e vedere se funziona per voi. – Hiro2k

0

Ok, ecco il mio nuovo metodo selectLockTest:

codice:

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; 
} 

Risultato:

[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 due SELECT pER AGGIORNAMENTO è stato eseguito (uno per filo), ma solo un aggiornamento è stato eseguito e il padrone di casa è stato thread_1 persisteva. È meglio, ma è il comportamento previsto?

Grazie

+1

non dovresti fare altre domande in forma di risposta. –

8

Si sono super-confusa, ma almeno le cose sembrano meglio dopo le modifiche.Ci sono diversi modi per farlo, ma il modo migliore che ho trovato è quello di utilizzare in realtà ResultSet.update* metodi di JDBC:

In primo luogo, è necessario preparare la vostra dichiarazione SELECT ... FOR UPDATE con l'argomento ResultSet.CONCUR_UPDATABLE, in questo modo:

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

Quindi, è necessario aggiornare in realtà la tabella utilizzando il ResultSet:

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

in terzo luogo, probabilmente è necessario utilizzare una transazione, che posso vedere nella vostra aggiornamento. Si spera che i tuoi metodi DbUtil.close non generino eccezioni, controlli nullo, ecc. Inoltre, se il tuo metodo diventa più complicato, dovresti avere anche la logica di rollback.

Non è necessario modificare my.ini per nessun motivo.

Problemi correlati