Sql 使用ORDER BY时数据库性能较差

Sql 使用ORDER BY时数据库性能较差,sql,postgresql,energy,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_

我正在与一家非营利机构合作,该机构正在美国绘制太阳能潜力图。不用说,我们有一个非常大的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;
选择A.s\u oid、A.s\u id、A.area\u acre、A.power\u peak、A.Near\u city、A.solar\u total
从全局_站点A交叉连接na_实用程序_线路B
式中(A.功率_峰值在1.0和100.0之间)
面积>=500
和A.solar_avg>=5.0
和A.pc_num=69
和B.fips_代码,如“%US06%”
和B.status='active'
和ST_内(ST_质心(A.wkb_几何体),ST_缓冲区(B.wkb_几何体),1000))
--按面积(英亩)订购
偏移量0限制11;

首先,我要看看如何创建索引,确保数据库被清空,增加数据库安装的共享缓冲区,工作内存设置

我建议创建一个面积指数。您可能需要查看以下内容:

我建议在高峰时间之外做这类事情,因为对于大量数据来说,这可能会有点密集。对于索引,您还必须注意一件事,那就是将它们放在时间表上,以确保随着时间的推移性能。同样,该时间表应在高峰时间之外


您可能想从一位同事那里了解一下这篇文章,以及他在索引方面随着时间的推移而降低数据库速度的经验:

首先要看的是,您在订购的字段上是否有索引。如果没有,添加一个将显著提高性能。我不太了解postgresql,但类似于:

CREATE INDEX area_acre ON global_site(area_acre)

如其他回复中所述,在处理大型数据集时,索引过程非常密集,因此在非高峰时段也要这样做。

如果a.area\u acre字段未索引,则可能会使其速度减慢。您可以使用EXPLAIN运行查询,以查看它在执行期间执行的操作

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

根据结果集的构建方式,在
area\u acre
字段上设置索引可能无助于排序(排序依据)。理论上,它可以通过使用
area\u acre
上的索引遍历
global\u site
表来生成结果集;在这种情况下,结果将按所需的顺序生成(并且在结果中生成11行后可能会停止)。如果它没有按这个顺序生成结果(看起来可能不是),那么该索引将无助于对结果进行排序

您可以尝试从查询中删除“交叉连接”。我怀疑这会有什么不同,但值得一试。因为WHERE子句涉及连接两个表(通过ST_in),所以我相信结果与内部连接相同。交叉连接语法的使用可能会导致优化器做出不希望的选择


否则(除了确保要筛选的字段存在索引之外),您可以对查询进行一些猜测。一个突出的条件是
面积=500
。这意味着查询引擎将考虑满足该条件的所有行。但是只取前11行。您可以尝试将其更改为
area\u acre>=500,area\u acre排序不是问题-事实上,排序的CPU和内存成本接近于零,因为Postgres具有Top-N排序,其中扫描结果集,同时保持一个仅包含Top-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
因此,您可以看到,前10名排序只会使愚蠢的快速计数(*)增加10毫秒,而实际排序的时间要长得多。这是一个非常简洁的特性,我经常使用它

现在没有解释和分析是不可能确定的,但我觉得真正的问题是交叉连接。基本上,您使用以下方法过滤两个表中的行:

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'
这意味着A的所有行都与B的所有行相匹配(因此,这个表达式将被计算很多次),使用一系列非常复杂、缓慢且cpu密集的函数

当然慢得可怕

当您删除ORDER BY时,postgres只是(偶然地)在开始处出现一堆匹配的行,输出这些行,并在达到限制后停止

这里有一个小例子:

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

select * from a cross join b where (a.b && b.b)     --- 0.28 s
在这里,1000000箱重叠(操作员&&)测试在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_形心(A.wkb_几何)
    • ST_缓冲区((B.wkb_几何体),1000)
在交叉连接过程中,重新计算那些速度慢的函数一百万次是没有意义的,所以请将结果存储在列中。使用触发器使其保持最新

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

    • ST_形心的边界框(A.wkb_几何体)
    • ST_缓冲区的边界框((B.wkb_几何体),1000)
  • 在框中添加要点索引

  • 添加将使用索引的框重叠测试(使用&&运算符)

  • 将您的ST_保持在其中,它将充当屏幕上的最终过滤器
    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