SQLite в многопоточном Java-приложении

Я написал 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);
         }
      }
   }
}

Вот вывод, когда я запускаю этот Java-код:

 [java] Sequential DB access:
 [java]    SQL Insert completed: 132
 [java]    SQL Insert completed: 133
 [java]    SQL Insert completed: 151
 [java]    SQL Insert completed: 134
 [java]    SQL Insert completed: 125
 [java] Concurrent DB access:
 [java]    SQL Insert completed: 116
 [java]    SQL Insert completed: 1117
 [java]    SQL Insert completed: 2119
 [java]    SQL Insert failed: 3001 SQLException: java.sql.SQLException: database locked
 [java]    SQL Insert completed: 3136

Последовательная вставка 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 (http://www.zentus.com/sqlitejdbcи http: //www.xerial.org/trac/Xerial/wiki/SQLiteJDBC) и оболочку sqlite4java. Каждый раз результаты были одинаковыми.Кто-нибудь знает библиотеку SQLite для Java, в которой нет такого поведения?

20
задан jlunavtgrad 22 May 2012 в 17:57
поделиться