SQLite在多线程的java应用程序中

前端之家收集整理的这篇文章主要介绍了SQLite在多线程的java应用程序中前端之家小编觉得挺不错的,现在分享给大家,也给大家做个参考。
我写了一个Java应用程序,它从多个线程偶尔地将事件记录到sqlite数据库。我注意到,我可以通过同时产生少量事件相对容易地触发sqlite的“数据库锁定”错误。这使我开始编写一个模拟最坏情况行为的测试程序,而且我在这个用例中表现出来的表现令人惊讶。下面的代码简单地将五个记录添加数据库中,首先顺序获取“控制”值。然后同时添加相同的五个记录。
  1. import java.sql.*;
  2.  
  3. public class Main {
  4. public static void main(String[] args) throws Exception {
  5. Class.forName("org.sqlite.JDBC");
  6. Connection conn = DriverManager.getConnection("jdbc:sqlite:test.db");
  7.  
  8. Statement stat = conn.createStatement();
  9. stat.executeUpdate("drop table if exists people");
  10. stat.executeUpdate("create table people (name,occupation)");
  11. conn.close();
  12.  
  13. sqlTask tasks[] = {
  14. new sqlTask("Gandhi","politics"),new sqlTask("Turing","computers"),new sqlTask("Picaso","artist"),new sqlTask("shakespeare","writer"),new sqlTask("tesla","inventor"),};
  15.  
  16. System.out.println("Sequential DB access:");
  17.  
  18. Thread threads[] = new Thread[tasks.length];
  19. for(int i = 0; i < tasks.length; i++)
  20. threads[i] = new Thread(tasks[i]);
  21.  
  22. for(int i = 0; i < tasks.length; i++) {
  23. threads[i].start();
  24. threads[i].join();
  25. }
  26.  
  27. System.out.println("Concurrent DB access:");
  28.  
  29. for(int i = 0; i < tasks.length; i++)
  30. threads[i] = new Thread(tasks[i]);
  31.  
  32. for(int i = 0; i < tasks.length; i++)
  33. threads[i].start();
  34.  
  35. for(int i = 0; i < tasks.length; i++)
  36. threads[i].join();
  37. }
  38.  
  39.  
  40. private static class sqlTask implements Runnable {
  41. String name,occupation;
  42.  
  43. public sqlTask(String name,String occupation) {
  44. this.name = name;
  45. this.occupation = occupation;
  46. }
  47.  
  48. public void run() {
  49. Connection conn = null;
  50. PreparedStatement prep = null;
  51. long startTime = System.currentTimeMillis();
  52.  
  53. try {
  54. try {
  55. conn = DriverManager.getConnection("jdbc:sqlite:test.db");
  56. prep = conn.prepareStatement("insert into people values (?,?)");
  57.  
  58. prep.setString(1,name);
  59. prep.setString(2,occupation);
  60. prep.executeUpdate();
  61.  
  62. long duration = System.currentTimeMillis() - startTime;
  63. System.out.println(" sql Insert completed: " + duration);
  64. }
  65. finally {
  66. if (prep != null) prep.close();
  67. if (conn != null) conn.close();
  68. }
  69. }
  70. catch(sqlException e) {
  71. long duration = System.currentTimeMillis() - startTime;
  72. System.out.print(" sql Insert Failed: " + duration);
  73. System.out.println(" sqlException: " + e);
  74. }
  75. }
  76. }
  77. }

这是输出当我运行这个java代码

  1. [java] Sequential DB access:
  2. [java] sql Insert completed: 132
  3. [java] sql Insert completed: 133
  4. [java] sql Insert completed: 151
  5. [java] sql Insert completed: 134
  6. [java] sql Insert completed: 125
  7. [java] Concurrent DB access:
  8. [java] sql Insert completed: 116
  9. [java] sql Insert completed: 1117
  10. [java] sql Insert completed: 2119
  11. [java] sql Insert Failed: 3001 sqlException: java.sql.sqlException: database locked
  12. [java] sql Insert completed: 3136

顺序插入5条记录大约需要750毫秒,我期望并发插入大概花费相同的时间。但是你可以看到给定3秒的超时时间,甚至没有完成。我也在C中编写了一个类似的测试程序,使用sqlite的本机库调用,并在大部分同时插入的时候完成了同时插入。所以问题是我的java库。

这是运行C版本时的输出

  1. Sequential DB access:
  2. sql Insert completed: 126 milliseconds
  3. sql Insert completed: 126 milliseconds
  4. sql Insert completed: 126 milliseconds
  5. sql Insert completed: 125 milliseconds
  6. sql Insert completed: 126 milliseconds
  7. Concurrent DB access:
  8. sql Insert completed: 117 milliseconds
  9. sql Insert completed: 294 milliseconds
  10. sql Insert completed: 461 milliseconds
  11. sql Insert completed: 662 milliseconds
  12. sql Insert completed: 862 milliseconds

我尝试使用两个不同的JDBC驱动程序(http://www.zentus.com/sqlitejdbchttp://www.xerial.org/trac/Xerial/wiki/SQLiteJDBC)和sqlite4java包装器。每次结果相似。有没有人知道没有这种行为的java的sqlite库?

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

另外,sqlite不支持每行锁定(还有?)。基本上每个操作的整个数据库文件变为locked。如果您幸运,并且文件系统支持字节范围锁定,则多个读卡器可能会同时访问您的数据库,但您不应该假定这种行为。

核心sqlite库by default allows multiple threads to use the same connection concurrently没有问题。我认为任何理智的JDBC包装器也将允许Java程序中的行为,尽管我还没有实际尝试过。

所以你有两个解决方案:

>在所有线程之间共享相同的JDBC连接。
>由于sqlite开发人员似乎认为是threads are evil,你最好是有一个线程处理所有的数据库操作,并使用Java代码自行序列化DB任务…

您可能想看看this old question of mine – 似乎已经累积了几个提示,以提高sqlite中的更新性能

猜你在找的Sqlite相关文章