H2数据库:虽然使用索引,但查询速度慢

时间:2015-01-22 14:37:52

标签: database indexing h2

使用H2 1.3.176。

1)表格定义:

CREATE TABLE TEST(ID BIGINT PRIMARY KEY, ACCOUNT BIGINT, TXID BIGINT); 

2)将值插入表中:

INSERT INTO TEST SELECT X, RAND()*100, X FROM SYSTEM_RANGE(1, 1000000)

3)创建用于我的查询的索引:

CREATE Unique INDEX IDX_TEST_ACCOUNT_TXID ON `test` (account, txId DESC);

4)执行以下查询:

explain analyze
select txid from test where account=22 AND txid<9999999 order by txid desc limit 25

我得到以下执行计划:

SELECT
    TXID
FROM PUBLIC.TEST
    /* PUBLIC.IDX_TEST_ACCOUNT_TXID: ACCOUNT = 22
        AND TXID < 9999999
     */
    /* scanCount: 9867 */
WHERE (ACCOUNT = 22)
    AND (TXID < 9999999)
ORDER BY 1 DESC
LIMIT 25
/*
TEST.IDX_TEST_ACCOUNT_TXID read: 103
*/

问题:为什么H2需要扫描整个索引?我期待扫描计数为25,因为索引中的txid已经是降序,所以一旦H2在索引的account = 22分支中,它应该能够读取接下来的25个条目。如果表中有数百万个条目,这将导致查询速度变慢。即使H2必须在索引中搜索第一个匹配条目,我也希望这是一个O(log(N))算法,而不是扫描。 如果我没有列帐户做同样的事情(意味着表只包含id和txid),那么txid上的降序索引确实会导致扫描计数为25(使用查询“select txid from test where txid&lt; 9999999 order通过txid desc“)。 为什么附加列会破坏执行计划? 也许我不明白索引是如何工作的。有没有更好的方法来为我的查询定义索引?

1 个答案:

答案 0 :(得分:7)

我介绍了h2源代码,发现了什么问题:

在准备执行查询期间,h2尝试确定它是否可以使用索引进行排序和限制结果集。由于第一个索引列(帐户)不在order by子句中,因此h2认为它不能使用索引。这导致h2扫描整个索引获取所有行,然后排序和限制结果集。这是令人惊讶的,因为帐户的条件是一个“相等”的条件,所以h2应该意识到它确实可以使用索引进行排序和限制结果集。 解决方案是在order by子句中提供account列。因此查询应该是:

select txid from test where account=22 AND txid<9999999 order by account, txid desc limit 25

我得到了预期的执行计划

SELECT
    TXID
FROM PUBLIC.TEST
    /* PUBLIC.IDX_TEST_ACCOUNT_TXID: ACCOUNT = 22
        AND TXID < 9999999
     */
    /* scanCount: 25 */
WHERE (ACCOUNT = 22)
    AND (TXID < 9999999)
ORDER BY =ACCOUNT, 1 DESC
LIMIT 25
/* index sorted */

扫描计数仅为25:)