postgres查询高CPU需要很长时间才能运行

时间:2013-02-22 00:28:14

标签: postgresql postgresql-9.1

我有一个运行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
)

单独运行它们都返回一行,但加在一起它会查询整个数据集。

1 个答案:

答案 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上更快。

我有什么遗漏或应该注意配置吗?