我遇到MySQL的SELECT问题.. FOR UPDATE,这是我试图运行的查询:
SELECT * FROM tableName WHERE HostName='UnknownHost'
ORDER BY UpdateTimestamp asc limit 1 FOR UPDATE
在此之后,相关的线程将执行UPDATE并更改HostName,然后它应该解锁该行。
我正在运行一个多线程的java应用程序,所以3个线程正在运行这个SQL语句,但是当线程1运行它时,它不会将其结果从线程2&因此,线程2& 3得到相同的结果,他们可以更新同一行。
此外,每个线程都有自己的mysql连接。
我正在使用Innodb,事务隔离= READ-COMMITTED,并且在执行select for update之前自动提交已关闭
我可能会错过什么吗?或许还有更好的解决方案? 非常感谢。代码:
public BasicJDBCDemo()
{
Le_Thread newThread1=new Le_Thread();
Le_Thread newThread2=new Le_Thread();
newThread1.start();
newThread2.start();
}
线程:
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);
}
}
选择:
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;
}
请帮助......:
结果:
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 :(得分:9)
你非常困惑,但至少在编辑后事情看起来更好。有多种方法可以做到这一点,但我发现最好的方法是实际使用JDBC的ResultSet.update*
方法:
首先,您需要使用SELECT ... FOR UPDATE
参数准备ResultSet.CONCUR_UPDATABLE
语句,如下所示:
ps = conn.prepareStatement(query,
ResultSet.TYPE_FORWARD_ONLY,
ResultSet.CONCUR_UPDATABLE);
然后,您必须使用ResultSet实际更新表:
if(rs.next())
{
rs.updateString(columnIndex, "new_hostname");
rs.updateRow();
}
第三,您可能需要使用一个事务,我可以在您的更新中看到。希望你的DbUtil.close
方法不会抛出任何异常,检查null等等。另外,如果你的方法变得更复杂,你也应该有回滚逻辑。
您不必因任何原因修改my.ini
。
答案 1 :(得分:1)
您创建的选择更新的连接需要与用于执行更新的连接相同。否则它不是同一个事务的一部分,它会释放锁,所以你的其他线程也会开始执行它。所以在你的代码中你需要这样做:
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();
答案 2 :(得分:0)
Select语句不能产生工作结果集,该结果集是由引起排序的任何组或订单操作引起的。选择必须在表格上保持活动状态,而不会进行任何排序。