Запросы SQLite чрезвычайно медленны в Java - PullRequest
3 голосов
/ 24 декабря 2011

Поэтому я пытаюсь использовать SQLite с довольно простым SQL-запросом (для тех, кто не знаком с GLOB, он похож на LIKE):

SELECT * FROM dictionary where word GLOB '[paple][paple][paple][paple][paple]';

Я могу запустить это в SQLite Manager, и это займет около50 мс, чтобы получить все записи.Теперь я пишу следующий код на Java, и это занимает почти 1,5 секунды, что кажется смехотворно медленным по сравнению.Я понимаю, что это может занять немного больше времени, но 1450 мс - это недопустимо медленно:

Connection conn = DriverManager.getConnection("jdbc:sqlite:dictionary.sqlite");
Statement stat = conn.createStatement();

long start = System.currentTimeMillis();
ResultSet rs = stat.executeQuery("SELECT * FROM dictionary where word GLOB '[paple][paple][paple][paple][paple]';");

while (rs.next()) {
    System.out.println("word = " + rs.getString("word"));
}

rs.close();
conn.close();

long end = System.currentTimeMillis();
System.out.println("Took: " + (end - start));

У меня такое ощущение, что каждый раз, когда я вызываю ResultSet.next (), он должен повторно запрашивать базу данных, посколькуне сразу получаю все записи, но я не уверен на 100%.Я чувствую, что должен быть намного более эффективный способ сделать это.Поэтому мой вопрос: кто-нибудь знает, как улучшить Java-код, чтобы он был намного быстрее?

PS: я использую sqliteJDBC .Может ли реализация здесь замедлить меня?Просто мысль у меня была.

Ответы [ 4 ]

1 голос
/ 24 декабря 2011

Каждый раз, когда вы звоните ResultSet#getString(String), вы заставляете много работы делать.См. код драйвера JDBC для его внутренний метод RS#findColumn(String).Обратите внимание, что он не кэширует сопоставление имя-столбца-порядковый-индекс столбца.Для каждой строки в наборе результатов, который вы проверяете, вы сталкиваетесь с множественными операциями сравнения строк и преобразования регистра.

Попробуйте заменить использование ResultSet#getString(String) на ResultSet#getString(int).Сначала, в вне цикла while , определите индекс столбца, который вы хотите извлечь.(Обратите внимание, что было бы гораздо лучше заменить SELECT * явным списком столбцов, в этом случае вы уже знаете порядковый индекс каждого столбца.)

final int indexWord = rs.findColumn("word");

Затем, во время итерации,используйте предварительно определенный индекс:

// Avoid concatenating:
System.out.print("word = ");
System.out.println(rs.getString(indexWord));

Сообщите нам, оказывает ли эта оптимизация заметное влияние.

0 голосов
/ 04 января 2016

Довольно старый :), но у нас была точно такая же проблема: запрос, который возвращает ~ 1500 результатов, выполняется за 50-100 мс в CLite SQLite, выполняется за 40'000 мс с драйвером JDBC.

99% времени было потрачено на rs.next

Мы обновили библиотеку sqlite-jdbc с 3.7 до последней (3.8.11), и производительность была примерно умножена на 1000.

0 голосов
/ 25 февраля 2014

Я столкнулся с той же проблемой, используя небольшую базу данных. Мой код был похож на это:

public LinkedList<Person> getByType(Type type) {
    LinkedList<Person> list = new LinkedList<>();
    String query = "SELECT * FROM person WHERE type_id = " + String.valueOf(type.getId());

    try {

        ResultSet rs = executeQuery(query); // Just calls statement.executeQuery(query);
        logTimestamp("After executeQuery");

        while (rs.next()) {
            logTimestamp("After rs.next");

            Person person = buildPersonFromResultSet(rs); // Just instances a new Person(rs.getLong("id"), rs.getString("name"));
            logTimestamp("After buildPersonFromResultSet");

            list.add(person);
            logTimestamp("After list.add");

            // Each loop iteration takes less than 1 ms
        }

        // list.size() is 26

        logTimestamp("After the last rs.next"); // After the last rs.next(), it was taking 4 seconds!
    } catch (Exception e) {
        LOGGER.error("Could not list. Query=[" + query + "]", e);
    }

    return list;
}

В журналах с метками времени я заметил, что замедление на 4 секунды происходило только при последнем вызове метода rs.next(). Я взглянул на исходный код драйвера SQLite JDBC (https://bitbucket.org/xerial/sqlite-jdbc/src)) и увидел, что происходит много вещей, когда курсор «выборки» обнаруживает, что он находится в последней строке. Я пытался увеличить выборку оператора размер (как указано в других ответах), но безуспешно. Я слышал, что таблицы базы данных должны быть проиндексированы для облегчения этой работы. Когда я проверял свои таблицы, я был удивлен, потому что не было индексов в первичном и внешнем ключах. Инструменты базы данных по умолчанию не создают индексы, поэтому я сделал это, и теперь последняя итерация тоже занимает менее 1 мс.

Итак, подытожив:

Моя база данных SQLite не имела индексов. После создания их для первичного и внешнего ключей весь цикл занимает 20 мс вместо 4 секунд.

0 голосов
/ 24 декабря 2011

Код Java выглядит нормально для меня. Основная проблема заключается в том, что будет выполняться линейное сканирование таблиц, которое может быть довольно медленным в большой базе данных, а индекс для столбца word не поможет (или, по крайней мере, не сильно поможет).

Какую базовую версию SQLite вы используете? Использование текущей версии может позволить больше оптимизаций. (Я спрашиваю, потому что sqliteJDBC несколько лет, но SQLite встроен в драйвер - конечно, поскольку это встроенная база данных, а не сервер БД - и с тех пор было довольно много выпусков.)

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...