PostGIS:提高对复杂几何体的查询速度

时间:2018-02-22 14:55:24

标签: postgis query-performance

我是PostgreSQL和PostGIS的新手,但这个问题并非无足轻重。我正在使用PostgreSQL 9.5和PostGIS 2.2。

我需要运行一些耗费大量时间的查询。

首先,让我用非GIS术语解释这个问题:
基本上,我有一组数十万点分布在一个大约五十万平方公里(国家)的领土上。 在这片领土上,我有来自各种数据库的十几个区域。在每一组中,我都有几百到几千个区域。我想找出这些领域中的哪些点。

现在,我目前如何解决GIS方面的问题:

每组区域都是一个Postgresql表,其中包含 multipolygon 类型的几何列,并且具有前几百到几千条记录所解释的内容。
所有这些表都包含在模式 donnees 中,但我为这些操作使用了不同的模式,称为 traitements

因此,该过程将所有几何图形合并为一个几何图形,然后b /查找此几何图形中包含的点。

问题在于,如果步骤a /花费了合理的时间(几分钟),步骤b /需要永远。 我目前只处理我必须处理的点的样本(大约1%,即大约7000),并且在几个小时之后它没有完成(数据库连接最终超时)。 我通过将返回行数限制为10或50来进行测试运行,并且它仍然需要大约半小时。
如果你想知道的话,我正在使用带有4个CPU和8 Gb RAM的Linux Mint 18机器。

我在几何列上创建了索引。所有几何列都使用相同的SRID。

创建表格

CREATE TABLE traitements.sites_candidats (
    pkid serial PRIMARY KEY,
    statut varchar(255) NOT NULL,
    geom geometry(Point, 2154)
); 

CREATE UNIQUE INDEX ON traitements.sites_candidats (origine, origine_id ) ;
CREATE INDEX ON traitements.sites_candidats (statut);
CREATE INDEX sites_candidats_geométrie ON traitements.sites_candidats USING GIST ( geom );

CREATE TABLE  traitements.zones_traitements (
    pkid serial PRIMARY KEY,
    définition varchar(255) NOT NULL,
    geom geometry (MultiPolygon, 2154)
);
CREATE UNIQUE INDEX ON traitements.zones_traitements (définition) ;
CREATE INDEX zones_traitements_geométrie ON traitements.zones_traitements USING GIST ( geom );

请注意,我只在表 traitements 中指定了 geom 列的几何类型,因为我想指定SRID,但我不确定正确的语法是什么适用于任何类型的几何。也许" geom geometry(Geometry,2154)" ?

合并各组区域的所有几何图形:
如前所述,所有表都具有多边形类型的几何形状。 这是我用来合并其中一个表的所有几何的代码:

INSERT INTO traitements.zones_traitements
    ( définition,  , geom )
    VALUES
    (
        'first-level merge', 
        (
            SELECT ST_Multi(ST_Collect(dumpedGeometries)) AS singleMultiGeometry 
            FROM
            (
                SELECT ST_Force2D((ST_Dump(geom)).geom) AS dumpedGeometries
                FROM donnees.one_table
            ) AS dumpingGeometries
        )
    ) ;

我发现某些记录中的某些几何图形是3D的,这就是我使用_ST_Force2D _的原因。

我对所有表执行此操作,然后使用:

再次合并几何
INSERT INTO traitements.zones_traitements
    ( définition, geom )
    VALUES
    (
        'second-level merge',
        (
            SELECT ST_Multi(ST_Collect(dumpedGeometries)) AS singleMultiGeometry 
            FROM
            (
                SELECT (ST_Dump(geom)).geom AS dumpedGeometries 
                FROM traitements.zones_traitements
                WHERE définition != 'second-level merge'
            ) AS dumpingGeometries
        )
    ) ;

如前所述,这些查询需要几分钟,但没关系。

不是永远需要的查询:

SELECT pkid
    FROM traitements.sites_candidats AS sites
    JOIN (
        SELECT geom FROM traitements.zones_traitements 
        WHERE définition = 'zones_rédhibitoires' ) AS zones
    ON ST_Contains(zones.geom , sites.geom)
    LIMIT 50;

分析问题:
显然,子查询选择需要花费大量时间的点,而不是更新。 所以我在查询上运行了一个EXPLAIN(ANALYZE,BUFFERS):

EXPLAIN (ANALYZE, BUFFERS)
    SELECT pkid
    FROM traitements.sites_candidats AS sites
    JOIN (
        SELECT geom FROM traitements.zones_traitements 
        WHERE définition = 'second_level_merge' ) AS zones
    ON ST_Contains(zones.geom , sites.geom)
    LIMIT 10;

---------------------------------

"Limit  (cost=4.18..20.23 rows=1 width=22) (actual time=6052.069..4393634.244 rows=10 loops=1)"
"  Buffers: shared hit=1 read=688784"
"  ->  Nested Loop  (cost=4.18..20.23 rows=1 width=22) (actual time=6052.068..4391938.803 rows=10 loops=1)"
"        Buffers: shared hit=1 read=688784"
"        ->  Seq Scan on zones_traitements  (cost=0.00..1.23 rows=1 width=54939392) (actual time=0.016..0.016 rows=1 loops=1)"
"              Filter: (("définition")::text = 'zones_rédhibitoires'::text)"
"              Rows Removed by Filter: 17"
"              Buffers: shared hit=1"
"        ->  Bitmap Heap Scan on sites_candidats sites  (cost=4.18..19.00 rows=1 width=54) (actual time=6052.044..4391260.053 rows=10 loops=1)"
"              Recheck Cond: (zones_traitements.geom ~ geom)"
"              Filter: _st_contains(zones_traitements.geom, geom)"
"              Heap Blocks: exact=1"
"              Buffers: shared read=688784"
"              ->  Bitmap Index Scan on "sites_candidats_geométrie"  (cost=0.00..4.18 rows=4 width=0) (actual time=23.284..23.284 rows=3720 loops=1)"
"                    Index Cond: (zones_traitements.geom ~ geom)"
"                    Buffers: shared read=51"
"Planning time: 91.967 ms"
"Execution time: 4399271.394 ms"

我不知道如何阅读此输出。

尽管如此,我怀疑查询是如此之慢,因为通过将所有这些多边形合并为一个多边形而获得的几何体。

问题:
使用不同类型的几何图形来合并其他几何图形会更好吗,比如 GeometryCollection ? 在这种情况下,索引如何工作?

是否比ST_Contains()效率更高?

1 个答案:

答案 0 :(得分:0)

再次,谢谢。

我得出了与你的建议相同的结论:不是将所有数千个多边形合并成一个巨大的多边形,其bbox太大,将所有多边形分解为简单的多边形会更有效率ST_Dump并将这些插入到具有适当索引的专用表中。 然而,要做到这一点,我首先必须纠正几何形状:某些多边形确实具有无效的几何形状。 St_MakeValid会使90%的有效多边形成为多边形,但其余部分将转换为GeometryCollections或MultilineStrings。为了纠正这些,我使用了ST_Buffer,缓冲区为0.01米,其结果是正确的多边形。 一旦完成,我的所有多边形都是有效的,我可以将它们转储成简单的多边形。

这样做,我将搜索时间减少了+/- 5000!

:d