我正在开发一个允许多个用户访问一个数据库 (MySQL) 的程序,并且在不同时间我得到一个 SQLException: Lock wait timeout exceeded
.
使用以下方法创建连接:
conn = DriverManager.getConnection(connString, username, password);
conn.setAutoCommit(false);
所有调用都通过这段代码:
try {
saveRecordInternal(record);
conn.commit();
} catch (Exception ex) {
conn.rollback();
throw ex;
}
在哪里saveRecordInternal
有一些内部逻辑,保存给定的记录。一路上的某个地方是我怀疑是问题的方法:
private long getNextIndex() throws Exception {
String query = "SELECT max(IDX) FROM MyTable FOR UPDATE";
PreparedStatement stmt = conn.prepareStatement(query);
ResultSet rs = stmt.executeQuery();
if (rs.next()) {
return (rs.getLong("IDX") + 1);
} else {
return 1;
}
}
此方法由 saveRecordInternal
调用如果需要,在它运行的某个地方。
由于目前我无法控制的原因,我不能使用自动增量索引,而且无论如何,某些内部程序逻辑需要要插入的索引。
我假设有 conn.commit()
或 conn.rollback()
called 足以释放锁,但显然不是。所以我的问题是 - 我应该使用 stmt.close()
或 rs.close()
里面getNextIndex
?是在提交或回滚事务之前释放锁,还是只是确保在调用 conn.commit()
时确实释放了锁?或 conn.rollback()
?
我还有什么遗漏/做错了吗?
编辑:在发生锁定时,所有连接的客户端似乎都有响应,当前没有任何查询正在进行,但关闭所有连接的客户端确实可以解决问题。这让我认为即使事务(据说?)通过提交或回滚结束,锁也会以某种方式保留下来。
最佳答案
即使不关闭 Statement
或 ResultSet
是个坏主意,但该函数似乎并不对您收到的错误负责。函数 getNextIndex()
正在创建本地 Statement
和 ResultSet
但没有关闭它。在那里关闭它们或在 saveRecordInternal()
中创建那些 Statement
和 ResultSet
对象并作为参数传递,如果在起点创建并重用则更好一次又一次。最后,在不再需要时关闭它们(按以下顺序 - ResultSet、Statement、Connection
)。
错误只是意味着某个数据库对象(Connection、Table、Row 等)上存在锁,而另一个线程/进程同时需要它但不得不等待(已经锁定)但由于时间超过等待超时预期的等待。
引用,How to Avoid Lock wait timeout exceeded exception.?了解更多关于这个问题。
总而言之,这是您的环境特定问题,需要在您的机器上调试并打开大量日志记录。
希望对您有所帮助!!
关于java - `SELECT ... FOR UPDATE`锁什么时候释放?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/34448811/