我在理论基础上工作,我想确保我所有的基础都被覆盖。
我已经阅读了很多关于使用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);
答案 0 :(得分:14)
您的代码基本上是正确的。发生死锁时引发的异常是SQLException
。异常的getSQLState()
方法提供了一个错误代码,提供有关实际错误的additional information。
您还应该在尝试之间等待很短的时间,以免过多地加载服务器。
正如您巧妙地猜到的那样,设置最大尝试次数,或者您可能最终处于无限循环中。
最终代码可能如下所示:
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()
或getSQLState()
提供的更多信息。
在我调用connection.commit()之后才抛出异常吗?
SQLException
包中几乎所有类的所有方法都可能抛出java.sql
。
是否应该在循环中运行,并且限制循环运行的次数?
是的,见上文。
我 [需要] 重新实例化
PreparedStatement
个对象吗?
显然,您不得在两个查询之间重新创建PreparedStatement
。您只需要在再次调用executeQuery()
之前为参数设置新值。当然,如果您需要执行另一个查询,则需要新的PreparedStatement
。
同样适用于
ResultSet
个对象
ResultSet
返回一个(新)Statement.executeQuery()
对象,它表示查询的结果。你永远不会自己创建这样的对象。理想情况下,您会尽快致电ResultSet.close()
以释放记忆。
我强烈建议您按照this tutorial的第二章(“处理SQL语句”)进行操作。