使用ORDER BY时DB性能不佳

时间:2011-05-03 17:47:47

标签: sql postgresql energy

我正在与一家在美国制定太阳能潜力的非盈利组织合作。不用说,我们有一个可笑的大型PostgreSQL 9数据库。运行如下所示的查询是快速的,直到order by行被取消注释,在这种情况下,相同的查询将永远运行(185毫秒没有排序,相比之下25分钟)。应该采取哪些措施来确保在更加可管理和合理的时间内运行此查询和其他查询?

select  A.s_oid, A.s_id, A.area_acre, A.power_peak, A.nearby_city, A.solar_total 
from global_site A cross join na_utility_line B
where (A.power_peak between 1.0 AND  100.0)
and A.area_acre >= 500
and A.solar_avg >= 5.0
AND A.pc_num <= 1000
and (A.fips_level1 = '06'  AND A.fips_country = 'US' AND A.fips_level2 = '025')
and B.volt_mn_kv >= 69
and B.fips_code like '%US06%'
and B.status = 'active'
and ST_within(ST_Centroid(A.wkb_geometry), ST_Buffer((B.wkb_geometry), 1000))
--order by A.area_acre
offset 0 limit 11;

7 个答案:

答案 0 :(得分:5)

排序不是问题 - 实际上排序的CPU和内存成本接近于零,因为Postgres具有Top-N排序,其中扫描结果集,同时保持最新只保留顶部的小排序缓冲区-N行。

select count(*) from (1 million row table)               -- 0.17 s
select * from (1 million row table) order by x limit 10; -- 0.18 s
select * from (1 million row table) order by x;          -- 1.80 s

所以你看到Top-10排序只增加10毫秒到一个哑快速计数(*)而不是更长的真实排序。这是一个非常简洁的功能,我经常使用它。

现在没有EXPLAIN ANALYZE就可以确定,但我的感觉是真正的问题是交叉连接。基本上你使用以下方法过滤两个表中的行:

where (A.power_peak between 1.0 AND  100.0)
and A.area_acre >= 500
and A.solar_avg >= 5.0
AND A.pc_num <= 1000
and (A.fips_level1 = '06'  AND A.fips_country = 'US' AND A.fips_level2 = '025')

and B.volt_mn_kv >= 69
and B.fips_code like '%US06%'
and B.status = 'active'

行。我不知道在两个表中选择了多少行(只有EXPLAIN ANALYZE会告诉),但它可能很重要。了解这些数字会有所帮助。

然后我们遇到了最糟糕的CROSS JOIN条件:

and ST_within(ST_Centroid(A.wkb_geometry), ST_Buffer((B.wkb_geometry), 1000))

这意味着A的所有行都与B的所有行匹配(因此,这个表达式将被大量计算),使用一堆相当复杂,缓慢且CPU密集的函数。

当然它非常慢!

当您删除ORDER BY时,postgres刚出现(偶然?),在开始时有一堆匹配的行,输出那些,并且自达到LIMIT后停止。

这是一个小例子:

表a和b是相同的,包含1000行,以及BOX类型的列。

select * from a cross join b where (a.b && b.b)     --- 0.28 s

这里1000000个盒重叠(操作员和&amp;&amp;)测试在0.28秒内完成。生成测试数据集,以便结果集只包含1000行。

create index a_b on a using gist(b);
create index b_b on a using gist(b);
select * from a cross join b where (a.b && b.b)     --- 0.01 s

这里索引用于优化交叉连接,速度很荒谬。

您需要优化几何匹配。

  • 添加将缓存的列:
    • ST_Centroid(A.wkb_geometry)
    • ST_Buffer((B.wkb_geometry),1000)

在CROSS JOIN期间重新计算这些慢速函数一百万次没有任何要点,因此将结果存储在一列中。使用触发器使它们保持最新。

  • 添加将缓存的BOX类型的列:

    • ST_Centroid的边界框(A.wkb_geometry)
    • ST_Buffer的边界框((B.wkb_geometry),1000)
  • 在BOX上添加gist索引

  • 添加一个Box重叠测试(使用&amp;&amp;运算符),它将使用索引

  • 保留您的ST_Within,它将作为传递的行的最终过滤器

也许您可以只索引ST_Centroid和ST_Buffer列...并使用(索引)“包含”运算符,请参见此处:

http://www.postgresql.org/docs/8.2/static/functions-geometry.html

答案 1 :(得分:2)

我建议在area_acre上创建一个索引。您可能需要查看以下内容:http://www.postgresql.org/docs/9.0/static/sql-createindex.html

我建议在高峰时段之外做这类事情,因为这可能会对大量数据造成一定程度的影响。您需要关注索引的一件事是rebuilding它们按计划进行,以确保长期性能。这个时间表应该在高峰时段之外。

您可能希望看一下SO'er的这篇文章,以及他随着时间的推移使用索引减缓数据库的经验:Why does PostgresQL query performance drop over time, but restored when rebuilding index

答案 2 :(得分:1)

如果A.area_acre字段未编入索引,可能会降低其速度。您可以使用EXPLAIN运行查询以查看执行期间的操作。

答案 3 :(得分:0)

首先,我会考虑创建索引,确保数据库正在被清理,增加db安装的共享缓冲区,work_mem设置。

答案 4 :(得分:0)

首先要看的是你是否在你所订购的领域有一个索引。如果没有,添加一个将显着提高性能。我不知道postgresql那么好但类似于:

CREATE INDEX area_acre ON global_site(area_acre)

正如其他回复中所述,使用大型数据集时索引过程非常密集,因此在非高峰时也是如此。

答案 5 :(得分:0)

我不熟悉PostgreSQL优化,但听起来像使用ORDER BY子句运行查询时发生的事情是创建整个结果集,然后对其进行排序,然后前11行是取自该排序结果。如果没有ORDER BY,查询引擎可以按照自己喜欢的顺序生成前11行,然后就完成了。

area_acre字段上有一个索引很可能对排序(ORDER BY)没有帮助,具体取决于结果集的构建方式。理论上,它可以通过使用global_site上的索引遍历area_acre表来生成结果集。在这种情况下,结果将以所需的顺序生成(并且在结果中生成11行后可能会停止)。如果它不按该顺序生成结果(并且看起来可能不是),那么该索引将无助于对结果进行排序。

您可能尝试的一件事是从查询中删除“CROSS JOIN”。我怀疑这会有所作为,但值得一试。因为WHERE子句涉及连接两个表(通过ST_WITHIN),我相信结果与内连接相同。使用CROSS JOIN语法可能会导致优化器做出不合需要的选择。

否则(除了确保正在过滤的字段存在索引之外),您可以使用查询玩一点猜谜游戏。突出的一个条件是area_acre >= 500。这意味着查询引擎正在考虑满足该条件的所有行。但是后来只有前11行。您可以尝试将其更改为area_acre >= 500 and area_acre <= somevaluesomevalue是需要调整的猜测部分,以确保您至少获得11行。然而,这似乎是一件非常俗气的事情,所以我提到它有点沉默。

答案 6 :(得分:0)

您是否考虑过创建Expression based indexes以获得更好的连接和条件?