Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

JDBC Lock a row using SELECT FOR UPDATE, doesn't work

Tags:

java

sql

jdbc

I am having issues with MySQL's SELECT .. FOR UPDATE, here is the query I am trying to run:

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

After this, the concerned thread will do an UPDATE and change the HostName, which is then it should unlock the row.

I am running a multi-threaded java application, so 3 threads are running this SQL statement, but when thread 1 runs this, it doesn't lock its results from thread 2 & 3. Therefore threads 2 & 3 are getting the same results and they could update the same row.

Also each thread is on its own mysql connection.

I'm using Innodb, with transaction-isolation = READ-COMMITTED, and the Autocommit is off before executing the select for update

may I miss something? OR perhaps there is a better solution? Thanks a lot.

Code :

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

Thread :

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

Please help.... :

Result :

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
like image 683
Rachid Avatar asked Jan 13 '11 17:01

Rachid


People also ask

Does SELECT for update lock whole table?

The SELECT FOR UPDATE statement is used to order transactions by controlling concurrent access to one or more rows of a table. It works by locking the rows returned by a selection query, such that other transactions trying to access those rows are forced to wait for the transaction that locked the rows to finish.

Does SELECT for update block insert?

SQL Server only has the FOR UPDATE as part of a cursor. And, it only applies to UPDATE statements that are associated with the current row in the cursor. So, the FOR UPDATE has no relationship with INSERT .

Does mysql lock row on update?

FOR UPDATE or SELECT ... FOR SHARE transaction that requests the same locked row must wait until the blocking transaction releases the row lock. This behavior prevents transactions from updating or deleting rows that are queried for updates by other transactions.


1 Answers

You are super-confused, but at least things look better after your edits. There are multiple ways to do this, but the best way I've found is to actually use JDBC's ResultSet.update* methods:

First, you need to prepare your SELECT ... FOR UPDATE statement with the ResultSet.CONCUR_UPDATABLE argument, like this:

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

Then, you have to actually update the table using the ResultSet:

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

Third, you probably need to use a transaction, which I can see in your update. Hopefully, your DbUtil.close methods won't throw any exceptions, check for null, etc. Also, if your method gets any more complicated, you should have rollback logic in there, too.

You should not have to modify my.ini for any reason.

like image 103
Christopher Schultz Avatar answered Oct 19 '22 18:10

Christopher Schultz