我有一个运行PHP和postgres后端(9.1)的Web应用程序。
大部分重型DB提升工作都是通过postgres存储过程完成的。
应用程序中的一个进程是导入数据例程。存储过程在导入时非常密集,但在开发过程中可以在大约15秒内导入我的测试表(大约20行数据)。
这是在我本地桌面上的4核ubuntu VM上使用默认的postgres配置(将1GB ram分配给VM)完成的。我的CPU是Intel i7。
我在本地计算机上使用了pg_top,并且SELECT进程以大约60%的CPU使用率激增,然后在15秒内完成。
所以,我现在已经将应用程序部署到了一个实时环境,这是一个1and1专业服务器。 32核,64GB内存,2TB硬盘。非常昂贵且非常大的数字!
现在,在实时服务器上运行相同的导入例程需要6分钟,而postgres SELECT语句在100%CPU下运行大约需要6分钟。
我已经完成了许多postgres conf设置并增加了内存数量以匹配更高功率的盒子,但无论我改变什么,似乎都没有对极差的性能产生任何影响。
有没有人知道查询为什么会这么糟糕?
4核VM上只有15秒,内存为1GB
但在具有64GB内存的32核专用服务器上运行6分钟
有些事情显然已经被搞砸了,但我无法弄清楚它是什么:(
编辑:
好的这是我认为我已经找到问题的查询(在大数据集上花费50 / 60ms而不是在小数据集上花费10ms)
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)"
此外,执行此查询时未将任何索引添加到任何列。
另外,只需注意内部select语句在大数据集上运行大约需要10 / 20ms(所以它必须是更新?)它只更新了大量可用行中的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
)
单独运行它们都返回一行,但加在一起它会查询整个数据集。
答案 0 :(得分:1)
好的,我已经想出了一个更快捷的方法:
而不是这样做
WHERE e。“id”= 47425或e。“id”IN(...)
我可以删除OR语句,然后执行IN语句:
SELECT e."id" FROM artwork_entity e
WHERE 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)
)
现在差异是IN语句是深度> = 0而不是深度> 0。原因是我实际上存储了一个深度为0的实体的自引用关系。我想我在编写这个存储过程后添加了这种方式,所以当时它不可用。
无论如何,在这样做时它只搜索正确的行,结果是更快的查询(12ms而不是60ms)
这个答案可能对其他人没用!
修改强>
我确实说这是我的解决方案,总的来说它好多了。
但是,现在在实时服务器上进行导入需要50秒(而不是6分钟),但在本地虚拟机上仍然要快得多(使用相同数据集的时间为12秒)
postgres没有达到30%以上的CPU(在实时或本地服务器上),但由于某种原因,它在我的低功耗本地VM上更快。我有什么遗漏或应该注意配置吗?