什么时候`SELECT ... FOR UPDATE`锁被释放了?

时间:2015-12-24 07:14:47

标签: java mysql jdbc locking

我正在开发一个允许多个用户访问数据库(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()就足以释放锁定,但显然不是。所以我的问题是 - 我应该在stmt.close()内使用rs.close()还是getNextIndex?是否会在提交或回滚事务之前释放锁定,还是只是确保在调用conn.commit()conn.rollback()时确实已释放锁定?

还有什么我错过/完全错了吗?

编辑:在发生锁定时,所有连接的客户端似乎都是响应式的,当前没有任何查询,但关闭所有连接的客户端确实解决了问题。这导致我认为锁定以某种方式保留,即使事务(假设?)结束,或者通过提交或回滚。

3 个答案:

答案 0 :(得分:2)

即使没有关闭StatementResultSet也不是一个好主意,但该功能似乎不对您收到的错误负责。功能getNextIndex()正在创建本地StatementResultSet,但不会关闭它。在那里关闭那些或在Statement中创建ResultSetsaveRecordInternal()个对象,如果在起点中创建并作为参数或更好地传递,并一次又一次地重复使用。最后,在不再需要时关闭它们(按照以下顺序 - ResultSet, Statement, Connection)。

错误只是意味着某个数据库对象(连接,表,行等)上存在锁定,而另一个线程/进程同时需要它但必须等待(已经锁定)但等待时间超过期待的等待。

请参阅How to Avoid Lock wait timeout exceeded exception.?以了解有关此问题的更多信息。

总而言之,这是您特定于环境的问题,需要在您的计算机上调试并启用大量日志记录。

希望它有所帮助!!

答案 1 :(得分:2)

从上面的陈述中我看不到任何锁定仍未打开! 通常,只要调用提交或回滚,或者连接关闭,MySql就应该释放锁。

在你的情况下

SELECT max(IDX) FROM MyTable FOR UPDATE

会导致锁定整个表,但我认为这是预期的逻辑!您锁定表,直到插入新行,然后释放它以让其他人插入。 我会测试:

   SELECT IDX FROM MyTable FOR UPDATE Order by IDX Desc LIMIT 1

确保即使锁定单行也会保持锁定。

如果不是这种情况,由于表格非常大,我可能会锁定超时。

答案 2 :(得分:0)

所以,我认为这里发生的是:你的查询试图在某些表上执行,但该表被另一个进程锁定。因此,在旧锁不会从表中释放之前,您的查询将等待执行,一段时间后如果锁不会释放,您将获得锁定超时异常。

您还可以查看表级锁和行级锁。 简而言之:表级锁定锁定整个表,直到锁定在那里,您希望能够在同一个表上执行任何其他查询。 而 行级锁将锁定特定行,因此除了该行,您可以在表上执行查询。

您还可以检查表上是否有长时间运行的查询,并且由于您无法执行其他查询并获得异常。如何,检查这将根据数据库而有所不同,但您可以将其谷歌搜索到您的特定数据库,以查找查询以获取数据库上的打开连接或长时间运行的查询。