简体   繁体   English

JDBC使用SELECT FOR UPDATE锁定一行,不起作用

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

I am having issues with MySQL's SELECT .. FOR UPDATE, here is the query I am trying to run: 我遇到MySQL的SELECT问题.. FOR UPDATE,这是我试图运行的查询:

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. 在此之后,相关的线程将执行UPDATE并更改HostName,然后它应该解锁该行。

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. 我正在运行一个多线程java应用程序,所以3个线程正在运行这个SQL语句,但是当线程1运行它时,它不会从线程2和3锁定其结果。因此线程2和3得到相同的结果,他们可以更新同一行。

Also each thread is on its own mysql connection. 每个线程也都有自己的mysql连接。

I'm using Innodb, with transaction-isolation = READ-COMMITTED, and the Autocommit is off before executing the select for update 我正在使用Innodb,事务隔离= READ-COMMITTED,并且在执行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

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: 有多种方法可以做到这一点,但我发现最好的方法是实际使用JDBC的ResultSet.update*方法:

First, you need to prepare your SELECT ... FOR UPDATE statement with the ResultSet.CONCUR_UPDATABLE argument, like this: 首先,您需要使用ResultSet.CONCUR_UPDATABLE参数准备SELECT ... FOR UPDATE语句,如下所示:

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

Then, you have to actually update the table using the ResultSet: 然后,您必须使用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. 希望您的DbUtil.close方法不会抛出任何异常,检查null等。另外,如果您的方法变得更复杂,那么您也应该有回滚逻辑。

You should not have to modify my.ini for any reason. 你不应该因为任何原因修改my.ini

The connection you create that selects for update needs to be the same one that is used to do the update. 您创建的用于选择更新的连接需要与用于执行更新的连接相同。 Otherwise it's not part of the same transaction and it releases the lock, so your other threads start to execute it as well. 否则它不是同一个事务的一部分,它会释放锁,所以你的其他线程也会开始执行它。 So in your code You need to do this: 所以在你的代码中你需要这样做:

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

The Select statement can't result in a work result set which is caused by any group or order by operation that causes a sort. Select语句不能导致工作结果集由导致排序的操作引起的任何组或顺序引起。 The select must keep an active position on the table up which any sorting will not provide. 选择必须在桌面上保持活动位置,任何排序都不会提供。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM