【问题标题】:java.sql.Connection Isolation Leveljava.sql.Connection 隔离级别
【发布时间】:2013-01-25 04:59:05
【问题描述】:

我正在编写一些尖峰代码,但没有给我预期的结果。

我有一个基本上是几行计数器的表。其他表使用这些行来生成应该是唯一 ID 的内容。 当我运行下面的代码时,我例外的是到达 select 语句的第一个线程将获取该行或表上的锁,从而停止对唯一 id 值的所有读取或写入。然而,第二个线程总是在第一个线程之前完成,因为它被休眠了 1 秒,因此它们都读取相同的值并写入相同的值,所以它只增加一次,而不是我例外的两倍。

我的代码有什么问题,还是我对隔离级别的理解不正确?

我已删除样板代码。使用 MySQL 数据库的标准 sql.Connection。

private void incrementValue() {

        connection
                .setTransactionIsolation(Connection.TRANSACTION_SERIALIZABLE);

        statement = connection.createStatement();

        System.out.println(Thread.currentThread().getName()
                + " doing select");
        resultSet = statement.executeQuery("select * from counter");
        System.out.println(Thread.currentThread().getName()
                + "after select");
        if (counter++ == 0) {
            Thread.sleep(1000);
        }
        String incrementedValue = getIncrementedValue(resultSet);

        statement.executeUpdate("update counter set counter='"
                + incrementedValue + "'");


}

private String getIncrementedValue(ResultSet resultSet) throws SQLException {
    String value = "";
    if (resultSet.next()) {
        System.out.println(Thread.currentThread().getName() + "Value was "
                + resultSet.getString(1));

        value = (new Integer(resultSet.getString(1)) + 1) + "";

    }

    return value;

}

这是从 main 调用的

public static void main(String[] args) {
    DatabaseExample databaseExample = new DatabaseExample();

    Runnable runnable = new Runnable() {

        @Override
        public void run() {
            DatabaseExample databaseExample = new DatabaseExample();
            databaseExample.incrementValue();
        }
    };
    new Thread(runnable).start();

    databaseExample.incrementValue();
}

【问题讨论】:

  • 在这种情况下,必须在级别应用程序中进行锁定。只需使用关键字synchronized 同步您的方法。请注意,这会影响应用程序的性能。
  • 您可能想检查是否使用序列或身份生成您的 id,或使用 Hilo 方案。任何一个听起来都比你描述的要好。

标签: java sql isolation-level


【解决方案1】:

即使在 SERIALIZABLE 隔离级别,也可以并行进行多项选择。如果要在 select 子句的行上加锁,请使用select ... for update

参考资料:

http://dev.mysql.com/doc/refman/5.1/en/select.html

如果您将 FOR UPDATE 与使用页锁或行锁的存储引擎一起使用,则查询检查的行将被写锁定,直到当前事务结束。使用 LOCK IN SHARE MODE 设置一个共享锁,允许其他事务读取检查的行,但不能更新或删除它们。

http://dev.mysql.com/doc/refman/5.1/en/set-transaction.html#isolevel_serializable

可序列化

此级别类似于 REPEATABLE READ,但如果禁用自动提交,InnoDB 会将所有普通 SELECT 语句隐式转换为 SELECT ... LOCK IN SHARE MODE。

【讨论】:

  • JB Nizet:感谢您的回复。我希望有一个非数据库特定的解决方案,因为这需要在来自不同供应商的数据库上运行,但似乎没有。 FOR UPDATE 似乎可以在 MySQL 和 Oracle 以及使用 WITH (UPDLOCK) 的 SQL Server 上工作,同时捕获死锁异常并重新运行代码。
猜你喜欢
  • 1970-01-01
  • 2015-08-02
  • 2010-12-04
  • 1970-01-01
  • 1970-01-01
  • 2011-09-30
  • 1970-01-01
  • 2021-01-20
  • 2010-12-20
相关资源
最近更新 更多