0

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

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

4

1 回答 1

1

好的,我想出了一个更快的方法:

而不是这样做

其中 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 语句是 depth>=0 而不是 depth>0。原因是我实际上将实体的自引用关系存储在深度为 0 的位置。我想我是在编写此存储过程之后以这种方式添加的,因此当时它不可用。

无论如何,这样做它只搜索正确的行,结果是一个更快的查询(12 毫秒而不是 60 毫秒)

不过,这个答案可能对其他人没有用!

编辑:

我确实说过这是我的解决方案,总的来说它要好得多。

但是,现在在实时服务器上,导入需要 50 秒(而不是 6 分钟),但在我的本地 VM 上仍然要快得多(使用相同的数据集需要 12 秒)

postgres 没有达到 30% 以上的 CPU(在实时或本地服务器上),但由于某种原因,它在我的低功率本地 VM 上更快。

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

于 2013-02-23T23:17:47.133 回答