我有一个运行 PHP 和 postgres 后端(9.1)的 Web 应用程序。
大多数繁重的数据库提升工作都是通过 postgres 存储过程完成的。
应用程序中的一个过程是导入数据例程。存储过程在导入时非常密集,但在开发时可以在大约 15 秒内导入我的测试表(大约 20 行数据)。
这是在我的本地桌面上的 4 核 ubuntu VM 上完成的,默认 postgres 配置(分配给 VM 的 1GB 内存)。我的 CPU 是 Intel i7。
我在本地机器上使用了 pg_top,SELECT 进程在 CPU 使用率达到 60% 时达到峰值,然后在 15 秒内完成。
所以,我现在已经将应用程序部署到一个实时环境,这是一个 1and1 专业服务器。32 核,64GB 内存,2TB 硬盘。非常昂贵和非常大的数字!
现在,在实时服务器上运行相同的导入例程需要超过 6 分钟,而 postgres SELECT 语句在 100% CPU 下运行大约需要 6 分钟。
我已经通过了许多 postgres conf 设置并增加了内存数量以匹配更高功率的盒子,但无论我如何更改它似乎都不会对极差的性能产生任何影响。
有谁知道为什么查询会执行得这么差?
在具有 1GB 内存的 4 核 VM 上只需 15 秒
但是在具有 64GB 内存的 32 核专用服务器上需要 6 分钟
有些东西显然是搞砸了,但我不知道它是什么:(
编辑:
好的,这是我认为我已经确定问题的查询(在大型数据集上需要 50/60 毫秒,而不是在小数据集上需要 10 毫秒)
EXPLAIN UPDATE artwork_entity SET "updated_on"=NOW(), "category"='blah', "category:oid"=47425
WHERE artwork_entity."id" IN (
SELECT n."id" FROM (
SELECT e."id" FROM artwork_entity e
WHERE e."id"=47425 OR e."id" IN
(SELECT l."descendant_id" FROM artwork_relation l
LEFT JOIN artwork_entity e1 ON l."descendant_id"=e1."id"
WHERE l."depth">0 AND l."ancestor_id"=47425
AND (e1."category:oid"=(SELECT e2."category:oid" FROM artwork_entity e2 WHERE e2."id"=l."ancestor_id") OR e1."category:oid" IS NULL))
) AS n);
Update on artwork_entity (cost=3864.35..7743.46 rows=21118 width=451)"
-> Hash Semi Join (cost=3864.35..7743.46 rows=21118 width=451)"
Hash Cond: (artwork_entity.id = e.id)"
-> Seq Scan on artwork_entity (cost=0.00..3364.36 rows=42236 width=445)"
-> Hash (cost=3600.38..3600.38 rows=21118 width=10)"
-> Seq Scan on artwork_entity e (cost=24.84..3600.38 rows=21118 width=10)"
Filter: ((id = 47425) OR (hashed SubPlan 2))"
SubPlan 2"
-> Nested Loop Left Join (cost=0.00..24.83 rows=1 width=4)"
Filter: ((e1."category:oid" = (SubPlan 1)) OR (e1."category:oid" IS NULL))"
-> Index Scan using artwork_relation_ancestor_id_descendant_id_key on artwork_relation l (cost=0.00..8.28 rows=1 width=8)"
Index Cond: (ancestor_id = 47425)"
Filter: (depth > 0)"
-> Index Scan using artwork_entity_pkey on artwork_entity e1 (cost=0.00..8.27 rows=1 width=8)"
Index Cond: (l.descendant_id = id)"
SubPlan 1"
-> Index Scan using artwork_entity_pkey on artwork_entity e2 (cost=0.00..8.27 rows=1 width=4)"
Index Cond: (id = l.ancestor_id)"
此外,执行此查询时没有向任何列添加索引。
另外,请注意内部选择语句只需要大约 10/20 毫秒即可在大型数据集上运行(所以它必须是更新?)它只更新大量可用行中的 2 行。
编辑2:
EXPLAIN SELECT e."id" FROM artwork_entity e
WHERE e."id"=47425 OR e."id" IN
(
SELECT l."descendant_id" FROM artwork_relation l
LEFT JOIN artwork_entity e1 ON l."descendant_id"=e1."id"
WHERE l."depth">0 AND l."ancestor_id"=47425
AND (e1."category:oid"=(SELECT e2."category:oid" FROM artwork_entity e2 WHERE e2."id"=l."ancestor_id") OR e1."category:oid" IS NULL)
)
然后它尝试为序列扫描获取 21k 行
但是,如果我将其分解为两个单独的查询,如下所示:
EXPLAIN SELECT e."id" FROM artwork_entity e
WHERE e."id"=47425
这只得到 1 行,然后是查询的其他部分
EXPLAIN SELECT l."descendant_id" FROM artwork_relation l
LEFT JOIN artwork_entity e1 ON l."descendant_id"=e1."id"
WHERE l."depth">0 AND l."ancestor_id"=47425
AND (e1."category:oid"=(SELECT e2."category:oid" FROM artwork_entity e2 WHERE e2."id"=l."ancestor_id") OR e1."category:oid" IS NULL)
也只得到 1 行,但如果第二个查询是 in 的一部分,那么它会尝试获取所有 21k 行。
怎么会?
编辑3:
将在初始扫描中返回 21k 行的语句简化为:
EXPLAIN SELECT e."id" FROM artwork_entity e
WHERE e."id"=47425 OR e."id" IN
(
SELECT l."descendant_id" FROM artwork_relation l
WHERE l."depth">0 AND l."ancestor_id"=47425
)
分别运行它们都返回单行,但加在一起它会查询整个数据集。