我使用 postgresql 和 psycopg2 构建了一个小型库存系统。一切都很好,除了当我想创建内容的汇总摘要/报告时,由于 count()'ing 和排序,我得到了非常糟糕的性能。
数据库架构如下:
创建表主机 ( id 序列主键, 名称 VARCHAR(255) ); 创建表项 ( id 序列主键, 说明文字 ); 创建表 host_item ( id 序列主键, host INTEGER REFERENCES hosts(id) ON DELETE CASCADE ON UPDATE CASCADE, item INTEGER REFERENCES items(id) ON DELETE CASCADE ON UPDATE CASCADE );
还有一些其他领域,但这些领域并不相关。
我想提取 2 个不同的报告: - 包含每个项目数量的所有主机列表,从最高到最低计数排序 - 包含每个主机数量的所有项目列表,从最高到最低计数排序
为此,我使用了 2 个查询:
具有主机计数的项目:
SELECT i.id, i.description, COUNT(hi.id) AS 计数 从项目 AS i LEFT JOIN host_item AS hi 开(i.id=hi.item) 按 i.id 分组 ORDER BY 计数 DESC 限制 10;
具有项目计数的主机:
选择 h.id, h.name, COUNT(hi.id) 作为计数 FROM 主机 AS h LEFT JOIN host_item AS hi 开(h.id=hi.host) 按隐藏组分组 ORDER BY 计数 DESC 限制 10;
问题是:查询在返回任何数据之前运行 5-6 秒。由于这是一个基于 Web 的应用程序,因此 6 秒是不可接受的。该数据库大量填充了大约 50k 主机、1000 个项目和 400 000 个主机/项目关系,并且在使用应用程序时(或者如果)可能会显着增加。
在玩了之后,我发现通过删除“ORDER BY count DESC”部分,两个查询都会立即执行而没有任何延迟(完成查询不到 20 毫秒)。
有什么办法可以优化这些查询,以便我可以毫不拖延地对结果进行排序?我正在尝试不同的索引,但是看到计数被计算出来,可以为此使用索引。我读过postgresql中的count()'ing很慢,但它的排序给我带来了问题......
我目前的解决方法是将上述查询作为每小时作业运行,将结果放入一个新表中,该表在计数列上有一个索引,以便快速查找。
我使用 Postgresql 9.2。
更新:按顺序查询计划:)
EXPLAIN ANALYZE
SELECT h.id, h.name, COUNT(hi.id) AS count
FROM hosts AS h
LEFT JOIN host_item AS hi
ON (h.id=hi.host)
GROUP BY h.id
ORDER BY count DESC
LIMIT 10;
Limit (cost=699028.97..699028.99 rows=10 width=21) (actual time=5427.422..5427.424 rows=10 loops=1)
-> Sort (cost=699028.97..699166.44 rows=54990 width=21) (actual time=5427.415..5427.416 rows=10 loops=1)
Sort Key: (count(hi.id))
Sort Method: top-N heapsort Memory: 25kB
-> GroupAggregate (cost=613177.95..697840.66 rows=54990 width=21) (actual time=3317.320..5416.440 rows=54990 loops=1)
-> Merge Left Join (cost=613177.95..679024.94 rows=3653163 width=21) (actual time=3317.267..5025.999 rows=3653163 loops=1)
Merge Cond: (h.id = hi.host)
-> Index Scan using hosts_pkey on hosts h (cost=0.00..1779.16 rows=54990 width=17) (actual time=0.012..15.693 rows=54990 loops=1)
-> Materialize (cost=613177.95..631443.77 rows=3653163 width=8) (actual time=3317.245..4370.865 rows=3653163 loops=1)
-> Sort (cost=613177.95..622310.86 rows=3653163 width=8) (actual time=3317.199..3975.417 rows=3653163 loops=1)
Sort Key: hi.host
Sort Method: external merge Disk: 64288kB
-> Seq Scan on host_item hi (cost=0.00..65124.63 rows=3653163 width=8) (actual time=0.006..643.257 rows=3653163 loops=1)
Total runtime: 5438.248 ms
EXPLAIN ANALYZE
SELECT h.id, h.name, COUNT(hi.id) AS count
FROM hosts AS h
LEFT JOIN host_item AS hi
ON (h.id=hi.host)
GROUP BY h.id
LIMIT 10;
Limit (cost=0.00..417.03 rows=10 width=21) (actual time=0.136..0.849 rows=10 loops=1)
-> GroupAggregate (cost=0.00..2293261.13 rows=54990 width=21) (actual time=0.134..0.845 rows=10 loops=1)
-> Merge Left Join (cost=0.00..2274445.41 rows=3653163 width=21) (actual time=0.040..0.704 rows=581 loops=1)
Merge Cond: (h.id = hi.host)
-> Index Scan using hosts_pkey on hosts h (cost=0.00..1779.16 rows=54990 width=17) (actual time=0.015..0.021 rows=11 loops=1)
-> Index Scan Backward using idx_host_item_host on host_item hi (cost=0.00..2226864.24 rows=3653163 width=8) (actual time=0.005..0.438 rows=581 loops=1)
Total runtime: 1.143 ms
更新:这个问题的所有答案都非常有助于学习和理解 Postgres 的工作原理。这个问题似乎没有任何明确的解决方案,但我非常感谢您提供的所有优秀答案,我将在以后的 Postgresql 工作中使用这些答案。非常感谢你们!