问题 Java多线程数据库访问[关闭]


我想问一下什么是多线程Java应用程序的最佳解决方案,以确保所有线程同步访问db。例如,每个线程代表单独的事务,并首先检查db的值,然后根据答案必须插入或更新数据库中的某些字段(注意,插入和提交应用程序之间的注意事项正在进行其他处理)。但问题是另一个线程可能在同一个表上做同样的事情。更具体的例子。线程T1启动事务,然后检查表ENTITY_TABLE是否输入代码为'111'如果找到更新其日期,如果未找到则插入新条目,然后提交事务。现在假设线程T2完全相同。现在几乎没有问题: 1. T1和T2检查db并找不到任何内容,并且都插入相同的条目。 2. T1检查db,查找具有旧日期的条目,但是在提交T2时已经更新了更新的日期。 3.如果我们使用缓存并同步访问缓存,则会出现问题:T1获取锁定检查db和缓存,如果未找到则添加到缓存,释放锁定,提交。 T2做同样的事情,发现缓存中的条目将提交。但T1事务失败并被支持。现在T2处于糟糕的状态,因为它应该插入ENTITY_TABLE但不知道。 还有更多?

我正在努力创建简单的自定义缓存,同步和解决问题3.但我感兴趣的是,有一些更简单的解决方案吗?有没有人必须解决类似的问题?你是怎么做到的?


8057
2018-02-02 16:25


起源



答案:


这应该主要通过配置所需的DB来处理 交易隔离 水平。然后在此基础上,您需要选择锁定策略(乐观 或者悲观)。

如果没有事务隔离,您将很难尝试仅在Java域中确保事务完整性。特别是考虑到即使数据库当前仅从您的Java应用程序访问,这可能在将来发生变化。

现在关于选择哪个隔离级别,从您的描述中可能看起来您需要最高的隔离级别, 序列化。然而,在实践中,这趋于  由于广泛的锁定,成为一个真正的性能生猪。因此,您可能需要重新评估您的要求,以便针对您的具体情况找到隔离和性能的最佳平衡。


8
2018-02-02 16:36



谢谢你的回答。问题是应用程序必须非常高性能,它可能运行在16个甚至32个线程上。每个启动事务,执行大量处理,然后获取结果并在提交步骤上执行插入和更新 - 所有这些都在jdbc批处理中(用于性能)。然后提交事务。无论如何在我的情况下,可序列化级别对于32个线程来说太多了。我希望我能用Java应用程序中的缓存来解决这个问题。 - nesvarbu


答案:


这应该主要通过配置所需的DB来处理 交易隔离 水平。然后在此基础上,您需要选择锁定策略(乐观 或者悲观)。

如果没有事务隔离,您将很难尝试仅在Java域中确保事务完整性。特别是考虑到即使数据库当前仅从您的Java应用程序访问,这可能在将来发生变化。

现在关于选择哪个隔离级别,从您的描述中可能看起来您需要最高的隔离级别, 序列化。然而,在实践中,这趋于  由于广泛的锁定,成为一个真正的性能生猪。因此,您可能需要重新评估您的要求,以便针对您的具体情况找到隔离和性能的最佳平衡。


8
2018-02-02 16:36



谢谢你的回答。问题是应用程序必须非常高性能,它可能运行在16个甚至32个线程上。每个启动事务,执行大量处理,然后获取结果并在提交步骤上执行插入和更新 - 所有这些都在jdbc批处理中(用于性能)。然后提交事务。无论如何在我的情况下,可序列化级别对于32个线程来说太多了。我希望我能用Java应用程序中的缓存来解决这个问题。 - nesvarbu


如果要从数据库中SQL SQL行,然后再更新同一行,则作为Java开发人员有2个选择。

  1. 用ROWLOCK选择,或者不管用什么 行锁语法适用于您的 特定的数据库。

  2. 选择行,进行处理, 就在你准备好之前 更新,再次选择行以查看 如果任何其他线程发生了变化 如果两个SELECTS返回相同的 值,更新。如果没有,扔一个 错误或再次处理。


2
2018-02-02 16:56





在处理使用Sqllite数据库的多线程Java程序时,我遇到了这个问题。它使用文件锁定,所以我必须确保只有一个线程同时工作。

我基本上最终使用了synchronized。当ConnectionFactory返回数据库连接时,它还会返回一个锁定对象,在使用该连接时应锁定该对象。因此,您可以手动执行同步锁定,或者创建下面的类的子类,以便为您执行此操作:

 /**
  * Subclass this class and implement the persistInTransaction method to perform
  * an update to the database.
  */
 public abstract class DBOperationInTransaction {

     protected Logger logger = Logger.getLogger(DBOperationInTransaction.class.getName());

     public DBOperationInTransaction(ConnectionFactory connectionFactory) {
         DBConnection con = null;

         try {
             con = connectionFactory.getConnection();

             if(con == null) {
                 logger.log(Level.SEVERE, "Could not get db connection");
                 throw new RuntimException("Could not get db connection");
             }

             synchronized (con.activityLock) {
                 con.connection.setAutoCommit(false);
                 persistInTransaction(con.connection);
                 con.connection.commit();
             }

         } catch (Exception e) {
             logger.log(Level.SEVERE, "Failed to persist data:", e);
             throw new RuntimeException(e);
         } finally {
             if(con != null) {
                 //Close con.connection silently.
             }
         }
     }

     /**
      * Method for persisting data within a transaction. If any SQLExceptions
      * occur they are logged and the transaction is rolled back.
      * 
      * In the scope of the method there is a logger object available that any
      * errors/warnings besides sqlException that you want to log.
      * 
      * @param con
      *            Connection ready for use, do not do any transaction handling
      *            on this object.
      * @throws SQLException
      *             Any SQL exception that your code might throw. These errors
      *             are logged. Any exception will rollback the transaction.
      * 
      */
     abstract protected void persistInTransaction(Connection con) throws SQLException;

 }

和DBConnection结构:

 final public class DBConnection {
     public final Connection connection;
     public final String activityLock;

     public DBConnection(Connection connection, String activityLock) {
         this.connection = connection;
         this.activityLock = activityLock;
     }

 }

2
2018-02-03 07:05





另外,我认为你必须在查询之前锁定表。这将强制您的线程顺序操作。然后你的线程应该为他们必须等待锁定的事实做好准备,当然,锁定获取可能会超时。这可能会给您的应用程序带来相当大的瓶颈,并且您的线程都必须排队等待数据库资源。


1
2018-02-02 16:37





你面临的问题是 交易隔离

好像你需要让每个线程在where子句中锁定相关的行,这需要可序列化的隔离。


1
2018-02-02 16:37





你为什么要重新发明轮子?我建议使用OR映射器框架进行事务数据库访问(例如,像Hibernate或Eclipselink这样的JPA规范实现者)。您还可以添加Spring DAO来处理您的交易。然后你可以专注于业务逻辑,而不必担心这些低级别的东西。


1
2018-02-02 16:51