如何正确处理Java/JDBC中的InnoDB死锁?

xLi*_*ite 10 java mysql deadlock innodb database-deadlocks

我在这里从理论上讲,我想确保我的所有基础都被覆盖.

我已经阅读了很多关于使用Java的InnoDB以及无论您运行什么查询都会发生死锁的情况.虽然我对理论和最佳实践非常了解,但我对如何在发生死锁时实现重新发布事务的catch all机制几乎一无所知.

是否有特定的例外来听取意见?我只是在我打电话后抛出异常,connection.commit()或者一旦我执行了它就会发生PreparedStatement吗?事情是否应该循环运行,并且限制循环运行的次数?

我基本上只需要一个简单的Java代码示例,说明如何处理这个问题.因为我不确定在哪些因素,例如,我是否重新实例化PreparedStatement对象或先关闭它们等等,这一切都非常令人困惑.同去的ResultSet对象了.

编辑:我应该提到我正在处理事务,将自动提交设置为0等.

编辑2:我是否使用这个伪代码在正确的轨道上?我没有线索

do
{
    deadlock = false

    try
    {
        // auto commit = 0
        // select query
        // update query
        // delete query
        // commit transaction
    }
    catch (DeadLockSpecificException e)
    {
        deadlock = true
    }
    finally
    {
        // close resources? statement.close(), resultset.close() etc?
        // or do I reuse them somehow and close them after the do/while loop?
        // this stuff confuses me a lot too
    }
}
while (deadlock == true);
Run Code Online (Sandbox Code Playgroud)

Ran*_*eed 14

您的代码基本上是正确的.发生死锁时引发的异常是a SQLException.异常的getSQLState()方法提供了返回错误代码,该代码提供有关实际错误的其他信息.

您还应该在尝试之间等待很短的时间,以免过多地加载服务器.

正如您巧妙地猜到的那样,设置最大尝试次数,或者您可能最终处于无限循环中.

最终的代码可能如下所示:

boolean oops;
int retries = 5;
Connection c = null;
Statement s = null;
ResultSet rs = null;    

do
{
    oops = false;
    c = null;
    s = null;
    rs = null;
    try
    {
        c = openConnection();
        s = c.createStatement();
        rs = s.executeQuery("SELECT stuff FROM mytable");
        fiddleWith(rs);
    }
    catch (SQLException sqlex)
    {
        oops = true;
        switch(sqlex.getErrorCode()())
        {
            case MysqlErrorNumbers.ER_LOCK_DEADLOCK:
                // deadlock or lock-wait time-out occured
                break;
            ...
        }
        Thread.sleep(1000); // short delay before retry
    }
    finally
    {
        if (rs != null) try {
            rs.close();
        } catch (SQLException e) {
            // some error handler here
        }

        if (s != null) try {
            s.close();
        } catch (SQLException e) {
            // some error handler here
        }

        if (c != null) try {
            c.close();
        } catch (SQLException e) {
            // some error handler here
        }

    }
}
while (oops == true && retries-- > 0);
Run Code Online (Sandbox Code Playgroud)

显然,上面的代码是次优的.您可能希望区分连接时发生的错误和执行时的错误.您还可以检测到在发生一些错误之后,没有希望再次尝试(例如,错误的凭据或SQL语法错误).

你问了很多问题,但我会尽力回答这些问题:

是否有特定的例外来听取意见?

是的,见上文:SQLException是的,由getErrorCode()或提供更多信息getSQLState().

我调用connection.commit()后才会抛出异常吗?

SQLException可以通过java.sql包中的所有类的几乎所有方法抛出A.

事情应该在循环中运行,并且限制循环运行的次数?

是的,见上文.

我是否[需要]重新实例化PreparedStatement对象?

显然,您不能PreparedStatement在两个查询之间重新创建.您只需要在executeQuery()再次调用之前为参数设置新值.当然,如果您需要执行另一个查询,则需要新的查询PreparedStatement.

同去的ResultSet对象太

ResultSet返回一个(新)对象Statement.executeQuery(),它表示查询的结果.你永远不会自己创建这样的对象.理想情况下,您会ResultSet.close()尽快打电话来释放记忆.

我强烈建议您按照本教程的第二章("处理SQL语句")进行操作.

  • 实际上它通常是相反的*(如果我的理解是正确的)*因为它基本上是死锁中的2个事务.一个人正常继续,另一个遭遇死锁异常.因此,遭遇死锁的事务可以安全地重试,因为获胜的事务已经获得了它想要的锁定.无论如何,非常感谢你的帮助!享受赏金. (2认同)