11

我在这里以理论为基础,我想确保我的所有基础都被覆盖。

我已经阅读了很多关于 InnoDB with Java 的内容,以及无论您运行什么查询,死锁是如何发生的。尽管我对理论和最佳实践非常了解,但我对如何在发生死锁时实现重新发布事务的全部捕获机制几乎一无所知。

是否有特定的例外需要注意?异常仅在我调用后引发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);
4

1 回答 1

16

您的代码基本上是正确的。发生死锁时引发的异常是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);

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

您问了很多问题,但我会尽力回答所有问题:

是否有特定的例外需要注意?

是的,见上文:SQLException是那些,由getErrorCode()or提供的更多信息getSQLState()

只有在我调用 connection.commit() 后才抛出异常?

ASQLException可以被java.sql包中所有类的几乎所有方法抛出。

事情是否应该在一个循环中运行,并限制循环运行的次数?

是的,见上文。

[需要]重新实例化PreparedStatement对象吗?

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

ResultSet对象也是如此

一个(新)ResultSet对象由返回Statement.executeQuery(),它表示查询的结果。您自己永远不会创建这样的对象。理想情况下,您会ResultSet.close()尽快调用以释放内存。

我强烈建议您阅读本教程的第二章(“处理 SQL 语句”)。

于 2013-06-05T09:15:52.150 回答