且构网

分享程序员开发的那些事...
且构网 - 分享程序员编程开发的那些事

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

更新时间:2023-02-15 22:15:33

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

Your code is essentially correct. The exception raised when a dead lock occurs is a SQLException. The exception's getSQLState() method provides returns an error code that provides additional information about the actual error.

您还应该在两次尝试之间等待一小段时间,以免服务器负载过多.

You should also wait a short amount of time between attemps, so as not to load your server too much.

您很聪明地猜到了,设置最大尝试次数,否则可能会陷入无限循环.

As you cleverly guessed, set a maximum number of attempts, or you might end-up in an infinite loop.

最终代码如下:

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语法错误)的希望.

Obviously the above code is sub optimal. You may want to differentiate errors taking place at connection time and errors at execution time. You could also detect that after some errors, there is little hope that another attempt will work (eg. wrong credentials or SQL syntax error).

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

You asked a lot of questions, but I will try to answer them all:

是否有特定的例外需要倾听?

Are there specific exceptions to listen out for?

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

Yes, see above: SQLException's are the ones, with more information provided by getErrorCode() or getSQLState().

是否仅在调用connection.commit()之后引发异常?

Is the exception only thrown after I call connection.commit()?

java.sql包中几乎所有类的所有方法都可能抛出SQLException.

A SQLException could be thrown by virtually all methods of all classes from the java.sql package.

事物是否应该在循环中运行,并限制循环运行多少次?

Should things be running in a loop with a limit to how many times the loop runs?

是的,请参见上文.

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

Do I [need to] re-instantiate PreparedStatement objects?

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

Obviously you must not re-create a PreparedStatement between two queries. You just need to set new values to your parameters before calling executeQuery() again. Of course if you need to execute another query, then a new PreparedStatement is required.

ResultSet对象也是如此

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

A (new) ResultSet object is returned by Statement.executeQuery(), which represents the result of the query. You never create such an object yourself. Ideally you will call ResultSet.close() as soon as possible to release memory.

我强烈建议您遵循本教程(处理SQL语句" ).

I strongly advise you to follow the second chapter of this tutorial ("Processing SQL Statements").