SQLite:大表上的 COUNT 速度慢

标签 sql database performance sqlite

我在使用 SELECT COUNT(*) 对大型表进行 SQLite 时遇到性能问题。

由于我还没有收到可用的答案并且我做了一些进一步的测试,所以我编辑了我的问题以纳入我的新发现。

我有 2 个表:

CREATE TABLE Table1 (
Key INTEGER NOT NULL,
... several other fields ...,
Status CHAR(1) NOT NULL,
Selection VARCHAR NULL,
CONSTRAINT PK_Table1 PRIMARY KEY (Key ASC))

CREATE Table2 (
Key INTEGER NOT NULL,
Key2 INTEGER NOT NULL,
... a few other fields ...,
CONSTRAINT PK_Table2 PRIMARY KEY (Key ASC, Key2 ASC))

Table1 约有 800 万条记录,Table2 约有 5100 万条记录,数据库文件超过 5GB。

Table1还有2个索引:

CREATE INDEX IDX_Table1_Status ON Table1 (Status ASC, Key ASC)
CREATE INDEX IDX_Table1_Selection ON Table1 (Selection ASC, Key ASC)

“状态”是必填字段,但只有 6 个不同的值,“选择”不是必需的,只有大约 150 万个不同于 null 的值,只有大约 60 万个不同的值。

我对两个表都做了一些测试,你可以看到下面的时间,我为每个请求 (QP) 添加了“解释查询计划”。我将数据库文件放在 USB 内存棒上,这样我就可以在每次测试后将其删除,并在不受磁盘缓存干扰的情况下获得可靠的结果。 USB 上的一些请求速度更快(我想是由于缺少寻道时间),但有些请求速度较慢(表扫描)。

SELECT COUNT(*) FROM Table1
    Time: 105 sec
    QP: SCAN TABLE Table1 USING COVERING INDEX IDX_Table1_Selection(~1000000 rows)
SELECT COUNT(Key) FROM Table1
    Time: 153 sec
    QP: SCAN TABLE Table1 (~1000000 rows)
SELECT * FROM Table1 WHERE Key = 5123456
    Time: 5 ms
    QP: SEARCH TABLE Table1 USING INTEGER PRIMARY KEY (rowid=?) (~1 rows)
SELECT * FROM Table1 WHERE Status = 73 AND Key > 5123456 LIMIT 1
    Time: 16 sec
    QP: SEARCH TABLE Table1 USING INDEX IDX_Table1_Status (Status=?) (~3 rows)
SELECT * FROM Table1 WHERE Selection = 'SomeValue' AND Key > 5123456 LIMIT 1
    Time: 9 ms
    QP: SEARCH TABLE Table1 USING INDEX IDX_Table1_Selection (Selection=?) (~3 rows)

如您所见,计数非常慢,但正常选择很快(第二个除外,它用了 16 秒)。

表2也是如此:

SELECT COUNT(*) FROM Table2
    Time: 528 sec
    QP: SCAN TABLE Table2 USING COVERING INDEX sqlite_autoindex_Table2_1(~1000000 rows)
SELECT COUNT(Key) FROM Table2
    Time: 249 sec
    QP: SCAN TABLE Table2 (~1000000 rows)
SELECT * FROM Table2 WHERE Key = 5123456 AND Key2 = 0
    Time: 7 ms
    QP: SEARCH TABLE Table2 USING INDEX sqlite_autoindex_Table2_1 (Key=? AND Key2=?) (~1 rows)

为什么 SQLite 不在 Table1 的主键上使用自动创建的索引? 为什么他在Table2上使用auto-index的时候还是要花很多时间?

我在 SQL Server 2008 R2 上创建了具有相同内容和索引的相同表,计数几乎是即时的。

下面的评论之一建议对数据库执行 ANALYZE。我做了,花了 11 分钟才完成。 之后,我再次运行了一些测试:

SELECT COUNT(*) FROM Table1
    Time: 104 sec
    QP: SCAN TABLE Table1 USING COVERING INDEX IDX_Table1_Selection(~7848023 rows)
SELECT COUNT(Key) FROM Table1
    Time: 151 sec
    QP: SCAN TABLE Table1 (~7848023 rows)
SELECT * FROM Table1 WHERE Status = 73 AND Key > 5123456 LIMIT 1
    Time: 5 ms
    QP: SEARCH TABLE Table1 USING INTEGER PRIMARY KEY (rowid>?) (~196200 rows)
SELECT COUNT(*) FROM Table2
    Time: 529 sec
    QP: SCAN TABLE Table2 USING COVERING INDEX sqlite_autoindex_Table2_1(~51152542 rows)
SELECT COUNT(Key) FROM Table2
    Time: 249 sec
    QP: SCAN TABLE Table2 (~51152542 rows)

如您所见,查询花费了相同的时间(除了查询计划现在显示的是实际行数),只是较慢的选择现在也很快。

接下来,我在 Table1 的 Key 字段上创建了一个额外的索引,它应该对应于自动索引。我在原始数据库上做了这个,没有分析数据。创建这个索引用了 23 多分钟(记住,这是在 U 盘上)。

CREATE INDEX IDX_Table1_Key ON Table1 (Key ASC)

然后我再次运行测试:

SELECT COUNT(*) FROM Table1
    Time: 4 sec
    QP: SCAN TABLE Table1 USING COVERING INDEX IDX_Table1_Key(~1000000 rows)
SELECT COUNT(Key) FROM Table1
    Time: 167 sec
    QP: SCAN TABLE Table2 (~1000000 rows)
SELECT * FROM Table1 WHERE Status = 73 AND Key > 5123456 LIMIT 1
    Time: 17 sec
    QP: SEARCH TABLE Table1 USING INDEX IDX_Table1_Status (Status=?) (~3 rows)

如您所见,索引对 count(*) 有帮助,但对 count(Key) 没有帮助。

最后,我使用列约束而不是表约束创建了表:

CREATE TABLE Table1 (
Key INTEGER PRIMARY KEY ASC NOT NULL,
... several other fields ...,
Status CHAR(1) NOT NULL,
Selection VARCHAR NULL)

然后我再次运行测试:

SELECT COUNT(*) FROM Table1
    Time: 6 sec
    QP: SCAN TABLE Table1 USING COVERING INDEX IDX_Table1_Selection(~1000000 rows)
SELECT COUNT(Key) FROM Table1
    Time: 28 sec
    QP: SCAN TABLE Table1 (~1000000 rows)
SELECT * FROM Table1 WHERE Status = 73 AND Key > 5123456 LIMIT 1
    Time: 10 sec
    QP: SEARCH TABLE Table1 USING INDEX IDX_Table1_Status (Status=?) (~3 rows)

虽然查询计划是一样的,但是时代已经好了很多。这是为什么?

问题是 ALTER TABLE 不允许转换现有表,而且我有很多现有数据库无法转换为这种形式。此外,使用列约束而不是表约束对 Table2 不起作用。

有人知道我做错了什么以及如何解决这个问题吗?

我使用 System.Data.SQLite 版本 1.0.74.0 创建表并运行我使用 SQLiteSpy 1.9.1 的测试。

谢谢,

马克

最佳答案

如果您还没有DELETE任何记录,做:

SELECT MAX(_ROWID_) FROM "table" LIMIT 1;

将避免全表扫描。

请注意 _ROWID_ is a SQLite identifier .

关于SQLite:大表上的 COUNT 速度慢,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/8988915/

相关文章:

sql - 如何识别与 sybase 数据库中的表关联的触发器?

java - Spring Data 未更新最新数据

sql-server - 如何替换 SQL Server 中逗号分隔字符串列中的值

database - 如何使用 django 更新会计应用程序中的余额?

sql - 员工排类系统

java - 使用 Spring Data JPA 运行存储过程时出现问题 - SQLServerException : The column name id is not valid

mysql - 一个非常简单的 UPDATE InnoDB 查询花费太多

java - 对于多个RecyclerView来说,Adapter实现的良好且高效的方式是什么?

java - 如何处理不同计算机之间的 Java 2D 性能差异?

performance - Postgres函数优化