【发布时间】:2012-10-16 15:57:27
【问题描述】:
我使用 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 count 从项目 AS i LEFT JOIN host_item AS hi 开(i.id=hi.item) 按 i.id 分组 ORDER BY 计数 DESC 限制 10;
具有项目数的主机:
SELECT h.id, h.name, COUNT(hi.id) AS 计数 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 的工作中使用这些答案。非常感谢你们!
【问题讨论】:
-
您能否同时发布原始查询和删除了
ORDER BY子句的查询的EXPLAIN ANALYZE输出? -
根据要求更新了分析数据。一位朋友在 MS SQL Server 上运行了相同的查询,并表示由于更好地跟踪统计信息,它立即返回相同的结果。也许是个愚蠢的问题,但与 Postgresql 相比,SQL Server 是否更擅长进行这种聚合?
-
请在查询/查询上运行
EXPLAIN (ANALYZE, BUFFERS)。将每个计划粘贴到explain.depesz.com 并在此处链接到它,这样我们就可以看到实际发生的情况。要求人们在没有计划和统计估计的情况下优化查询只会让你得到更多有根据的猜测。使用 PostgreSQL 9.2 的仅索引扫描和相对较小的数据集,我看不出有任何理由让这些扫描速度如此之慢。它还有助于解释您在哪台计算机上运行查询、存储是什么(RAID10?SSD?单硬盘?)、RAM 和非默认 postgresql.conf 设置。 -
@CraigRinger: With order by count Without order by count
-
@invictus ... 还有其他的吗?您是否更改了任何默认的
postgresql.conf设置? (你应该有)等?请立即查看之前的评论。
标签: sql postgresql aggregate postgresql-performance