簡體   English   中英

SQLite查詢在Java中速度極慢

[英]SQLite Queries Extremely Slow in Java

所以我試圖使用SQLite和一個相當基本的SQL查詢(對於那些不熟悉GLOB的人,它類似於LIKE):

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

我可以在SQLite Manager中運行它,它需要大約50ms來檢索所有記錄。 現在我用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 這里的實施能否減緩我的速度? 只是我的想法。

每次調用ResultSet#getString(String) ,都會強制執行大量工作。 請參閱JDBC驅動程序的代碼,了解其內部方法RS#findColumn(String) 請注意,它不會緩存column-name-to-column-ordinal-index映射。 對於您檢查的結果集中的每一行,您將遭受多個字符串比較和大小寫轉換操作。

嘗試使用ResultSet#getString(int)替換ResultSet#getString(String)使用。 首先, while循環之外的早期,找出要提取的列的索引。 (請注意,用明確的列列表替換SELECT *會好得多,在這種情況下,您已經知道每列的序數索引。)

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

然后,在迭代期間,使用先前確定的索引:

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

讓我們知道優化是否會產生明顯的影響。

我使用小型數據庫遇到了同樣的問題。 我的代碼與此類似:

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;
}

通過帶時間戳的日志,我注意到僅在最后一次調用rs.next()方法時發生了4秒的減速。 我看了一下SQLite JDBC驅動程序源代碼( https://bitbucket.org/xerial/sqlite-jdbc/src ),看到當“fetch”光標發現他在的時候發生了很多事情。最后一排。 我試圖增加語句的獲取大小(正如其他答案所指出的那樣),但沒有成功。 我聽說應該將數據庫表編入索引以簡化該工作。 當我檢查我的表時,我很驚訝,因為主鍵和外鍵中沒有索引。 默認情況下,某些數據庫工具不會創建索引,所以我這樣做了,現在最后一次迭代也需要不到1毫秒。

所以,總結一下:

我的SQLite數據庫沒有索引。 在為主鍵和外鍵創建它們之后,所有循環都需要20 ms而不是4秒。

相當老:)但我們有完全相同的問題:一個返回~1500結果的查詢,在SQLite CLI中執行50-100ms,使用JDBC驅動程序在40'000 ms內執行。

99%的時間花在rs.next上

我們將sqlite-jdbc庫從3.7升級到最新版(3.8.11),性能大致乘以1000。

Java代碼對我來說很好。 主要問題是它將進行線性表掃描,這在大型數據庫上可能相當慢,並且word列上的索引將無濟於事(或者至少不會有太大幫助)。

您正在使用的SQLite的基礎版本是什么? 使用當前版本可能會啟用更多優化。 (我問,因為sqliteJDBC已經存在了幾年,但SQLite已嵌入到驅動程序中 - 當然,因為它是一個嵌入式數據庫而不是數據庫服務器 - 從那時起就有不少版本。)

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM