2011-01-13 6 views
6
funktioniert

Ich habe Probleme mit MySQL SELECT .. FOR UPDATE, hier ist die Abfrage, die ich zu laufen versuche:JDBC Sperre eine Reihe FOR UPDATE mit SELECT, nicht

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

Danach wird die betreffende Thread wird ein UPDATE durchführen und den Hostnamen ändern, der dann die Zeile entsperren soll.

Ich führe eine Multi-threaded Java-Anwendung, so dass 3 Threads diese SQL-Anweisung ausführen, aber wenn Thread 1 das ausführt, sperrt es seine Ergebnisse von Thread 2 & 3. Daher Threads 2 & 3 bekommen die gleichen Ergebnisse und sie könnten die gleiche Zeile aktualisieren.

Auch jeder Thread ist auf einer eigenen mysql-Verbindung.

Ich bin mit InnoDB, mit transaktions Isolation = READ-COMMITTED und die Autocommit ausgeschaltet ist, bevor Sie die Auswahl für Update-Ausführung

kann ich etwas verpasst? ODER vielleicht gibt es eine bessere Lösung? Vielen Dank.

Code:

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

Thema:

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

} 

Select:

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

Bitte helfen ....:

Ergebnis:

 
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

Wie erreichen Sie diesen Code weise? –

+0

Was meinst du damit? – Rachid

Antwort

1

Die Verbindung, die Sie für die Aktualisierung auswählen, muss dieselbe sein, die für die Aktualisierung verwendet wird. Andernfalls ist es nicht Teil der gleichen Transaktion und es gibt die Sperre frei, sodass Ihre anderen Threads es ebenfalls ausführen. Also in Ihrem Code müssen Sie dies tun:

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

Hallo Hiro2k, danke für deine Antwort. Ich kann die Konfiguration des Datenbankservers nicht ändern, ich meine, ich kann die Datei my.ini nicht ändern. Aber im MySQL-Handbuch sagen sie, dass, wenn READ-COMMITTED gesetzt ist, nur der Index gesperrt wird, was in meinem Fall ok ist, weil die HostName-Spalte ein Index ist ... – Rachid

+0

Könnten Sie mir bitte Ihren Beispielcode senden? Ich vergleiche es mit meinem? – Rachid

+0

Hallo Rachid, ich kann den Code nicht teilen, aber ich habe gerade daran erinnert, dass Sie die Isolationsstufe auf der Verbindung mit setTransactionIsolation ändern können. Probieren Sie es aus und sehen Sie, ob es für Sie funktioniert. – Hiro2k

0

Ok, hier ist meine neue selectLockTest Methode:

Code:

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

Ergebnis:

[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 

Es bedeutet, zwei SELECT für UPDATE wurde ausgeführt (einer nach dem anderen), aber nur ein Update wurde ausgeführt und der thread_1-Host wurde beibehalten. Es ist besser, aber es ist das erwartete Verhalten?

Dank

+1

Sie sollten keine zusätzlichen Fragen in Form einer Antwort stellen. –

8

Sie sind super-verwirrt, aber zumindest die Dinge besser aussehen, nachdem Sie Ihre Änderungen.Es gibt mehrere Möglichkeiten, dies zu tun, aber der beste Weg, die ich gefunden habe ist, um tatsächlich ResultSet.update* Methoden JDBC des verwenden:

Zuerst müssen Sie Ihre SELECT ... FOR UPDATE Anweisung mit dem ResultSet.CONCUR_UPDATABLE Argument vorzubereiten, wie folgt aus:

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

Dann müssen Sie tatsächlich die Tabelle mit dem ResultSet aktualisieren:

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

Drittens haben Sie wahrscheinlich eine Transaktion verwenden müssen, die ich in Ihrem Update sehen. Hoffentlich werden Ihre DbUtil.close Methoden keine Ausnahmen werfen, nach Null usw. suchen. Auch wenn Ihre Methode komplizierter wird, sollten Sie auch Rollback-Logik dort haben.

Sie sollten my.ini aus irgendeinem Grund nicht ändern müssen.

Verwandte Themen