Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/extjs/3.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
多线程java应用程序中的SQLite_Java_Jdbc_Sqlite_Locked - Fatal编程技术网

多线程java应用程序中的SQLite

多线程java应用程序中的SQLite,java,jdbc,sqlite,locked,Java,Jdbc,Sqlite,Locked,我已经编写了一个java应用程序,它偶尔从多个线程将事件记录到SQLite数据库。我注意到,通过同时生成少量事件,可以相对容易地触发SQLite的“数据库锁定”错误。这促使我编写了一个模拟最坏情况行为的测试程序,我对SQLite在这个用例中的表现感到惊讶。下面发布的代码只是将五条记录添加到数据库中,首先按顺序获得“控制”值。然后同时添加相同的五条记录 import java.sql.*; public class Main { public static void main(String

我已经编写了一个java应用程序,它偶尔从多个线程将事件记录到SQLite数据库。我注意到,通过同时生成少量事件,可以相对容易地触发SQLite的“数据库锁定”错误。这促使我编写了一个模拟最坏情况行为的测试程序,我对SQLite在这个用例中的表现感到惊讶。下面发布的代码只是将五条记录添加到数据库中,首先按顺序获得“控制”值。然后同时添加相同的五条记录

import java.sql.*;

public class Main {
   public static void main(String[] args) throws Exception {
      Class.forName("org.sqlite.JDBC");
      Connection conn = DriverManager.getConnection("jdbc:sqlite:test.db");

      Statement stat = conn.createStatement();
      stat.executeUpdate("drop table if exists people");
      stat.executeUpdate("create table people (name, occupation)");
      conn.close();

      SqlTask tasks[] = {
         new SqlTask("Gandhi", "politics"),
         new SqlTask("Turing", "computers"),
         new SqlTask("Picaso", "artist"),
         new SqlTask("shakespeare", "writer"),
         new SqlTask("tesla", "inventor"),
      };

      System.out.println("Sequential DB access:");

      Thread threads[] = new Thread[tasks.length];
      for(int i = 0; i < tasks.length; i++)
         threads[i] = new Thread(tasks[i]);

      for(int i = 0; i < tasks.length; i++) {
         threads[i].start();
         threads[i].join();
      }

      System.out.println("Concurrent DB access:");

      for(int i = 0; i < tasks.length; i++)
         threads[i] = new Thread(tasks[i]);

      for(int i = 0; i < tasks.length; i++)
         threads[i].start();

      for(int i = 0; i < tasks.length; i++)
         threads[i].join();
   }


   private static class SqlTask implements Runnable {
      String name, occupation;

      public SqlTask(String name, String occupation) {
         this.name = name;
         this.occupation = occupation;
      }

      public void run() {
         Connection conn = null;
         PreparedStatement prep = null;
         long startTime = System.currentTimeMillis();

         try {
            try {
               conn = DriverManager.getConnection("jdbc:sqlite:test.db");
               prep = conn.prepareStatement("insert into people values (?, ?)");

               prep.setString(1, name);
               prep.setString(2, occupation);
               prep.executeUpdate();

               long duration = System.currentTimeMillis() - startTime;
               System.out.println("   SQL Insert completed: " + duration);
            }
            finally {
               if (prep != null) prep.close();
               if (conn != null) conn.close();
            }
         }
         catch(SQLException e) {
            long duration = System.currentTimeMillis() - startTime;
            System.out.print("   SQL Insert failed: " + duration);
            System.out.println(" SQLException: " + e);
         }
      }
   }
}
按顺序插入5条记录大约需要750毫秒,我希望并发插入所需的时间大致相同。但是你可以看到,在3秒钟的超时时间内,他们甚至都没有完成。我还用C编写了一个类似的测试程序,使用SQLite的本机库调用,同时插入的完成时间与并发插入的完成时间大致相同。所以问题在于我的java库

以下是我运行C版本时的输出:

Sequential DB access:
  SQL Insert completed: 126 milliseconds
  SQL Insert completed: 126 milliseconds
  SQL Insert completed: 126 milliseconds
  SQL Insert completed: 125 milliseconds
  SQL Insert completed: 126 milliseconds
Concurrent DB access:
  SQL Insert completed: 117 milliseconds
  SQL Insert completed: 294 milliseconds
  SQL Insert completed: 461 milliseconds
  SQL Insert completed: 662 milliseconds
  SQL Insert completed: 862 milliseconds
我用两个不同的JDBC驱动程序(and)和sqlite4java包装器尝试了这段代码。每次的结果都是相似的。有人知道SQLite java库没有这种行为吗?

这是一个问题,而不是任何java包装器。SQLite使用基于文件系统的锁实现进程间的并发访问同步,因为作为嵌入式数据库,它没有专用的进程(服务器)来调度操作。由于代码中的每个线程都创建自己与数据库的连接,因此它被视为一个单独的进程,通过基于文件的锁进行同步,这比任何其他同步方法都要慢得多

此外,SQLite不支持每行锁定(还没有?)。基本上,对于每个操作,整个数据库文件都将变为。如果幸运的话,并且您的文件系统支持字节范围锁,那么多个读卡器可以同时访问您的数据库,但您不应该假设这种行为

核心SQLite库没有问题。我假设任何一个健全的JDBC包装器都会在Java程序中允许这种行为,尽管我还没有真正尝试过

因此,您有两种解决方案:

  • 在所有线程之间共享相同的JDBC连接

  • 由于SQLite开发人员似乎认为,最好让一个线程处理所有数据库操作,并使用Java代码自己序列化DB任务


您可能想看一看-它似乎已经积累了一些技巧来提高SQLite中的更新性能。

我对多个线程使用相同的连接。
此外,我还必须使db写入方法同步,否则我仍然会收到bussy错误

hurray!使用相同的JDBC连接完全解决了这个问题。我现在看到性能优于或等于我的C代码。昨天我用上面的java程序尝试了这一点,但现在我意识到每次插入都会用一个新的连接覆盖我的连接。另请参见:
Sequential DB access:
  SQL Insert completed: 126 milliseconds
  SQL Insert completed: 126 milliseconds
  SQL Insert completed: 126 milliseconds
  SQL Insert completed: 125 milliseconds
  SQL Insert completed: 126 milliseconds
Concurrent DB access:
  SQL Insert completed: 117 milliseconds
  SQL Insert completed: 294 milliseconds
  SQL Insert completed: 461 milliseconds
  SQL Insert completed: 662 milliseconds
  SQL Insert completed: 862 milliseconds