【问题标题】:Gist index on Postgres/PostGIS still slowPostgres/PostGIS 上的 Gist 索引仍然很慢
【发布时间】:2021-08-01 02:39:37
【问题描述】:

我不是 Postgres/GIS 主题的专家,我对大型几何数据库(超过 2000 万条记录)有疑问。首先我的设置是这样的:

mmt=# select version();
-[ RECORD 1 ]-------------------------------------------------------------------------------------------------------------
version | PostgreSQL 13.2 (Debian 13.2-1.pgdg100+1) on x86_64-pc-linux-gnu, compiled by gcc (Debian 8.3.0-6) 8.3.0, 64-bit

mmt=# select PostGIS_Version();
-[ RECORD 1 ]---+--------------------------------------
postgis_version | 3.1 USE_GEOS=1 USE_PROJ=1 USE_STATS=1

我正在查询的表包含以下列:

mmt=# \d titles
                                              Table "public.titles"
        Column        |           Type           | Collation | Nullable |                 Default                 
----------------------+--------------------------+-----------+----------+-----------------------------------------
 ogc_fid              | integer                  |           | not null | nextval('titles_ogc_fid_seq'::regclass)
 wkb_geometry         | bytea                    |           |          | 
 timestamp            | timestamp with time zone |           |          | 
 end                  | timestamp with time zone |           |          | 
 gml_id               | character varying        |           |          | 
 validfrom            | character varying        |           |          | 
 beginlifespanversion | character varying        |           |          | 
 geom_bounding_box    | geometry(Geometry,4326)  |           |          | 
Indexes:
    "titles_pkey" PRIMARY KEY, btree (ogc_fid)
    "geom_idx" gist (geom_bounding_box)

geom_bounding_box 列包含 wkb_geometry 的边界框。我创建了该边界框列,因为 wkb 几何超出了 GIST 索引中项目的默认大小限制。其中一些是相当复杂的几何图形,由几十个点组成一个多边形。相反,使用边界框意味着我可以在该列上放置一个索引作为加快搜索速度的一种方式。至少理论上是这样。

我的搜索旨在查找位于给定点 100 米范围内的几何图形,如下所示,但这需要两分钟多的时间才能返回。我想在一秒钟内完成!:

select ogc_fid, web_geometry from titles where ST_DWithin(geom_bounding_box, 'SRID=4326;POINT(-0.145872 51.509691)'::geography, 100);

下面是一个基本的解释输出。我能做些什么来加快这件事的速度?

谢谢!

mmt=# explain select ogc_fid from titles where ST_DWithin(geom_bounding_box, 'SRID=4326;POINT(-0.145872 51.509691)'::geography, 100);
-[ RECORD 1 ]----------------------------------------------------------------------------------------------------------------------------------------------------------
QUERY PLAN | Gather  (cost=1000.00..243806855.33 rows=2307 width=4)
-[ RECORD 2 ]----------------------------------------------------------------------------------------------------------------------------------------------------------
QUERY PLAN |   Workers Planned: 2
-[ RECORD 3 ]----------------------------------------------------------------------------------------------------------------------------------------------------------
QUERY PLAN |   ->  Parallel Seq Scan on titles  (cost=0.00..243805624.63 rows=961 width=4)
-[ RECORD 4 ]----------------------------------------------------------------------------------------------------------------------------------------------------------
QUERY PLAN |         Filter: st_dwithin((geom_bounding_box)::geography, '0101000020E61000006878B306EFABC2BF6308008E3DC14940'::geography, '100'::double precision, true)
-[ RECORD 5 ]----------------------------------------------------------------------------------------------------------------------------------------------------------
QUERY PLAN | JIT:
-[ RECORD 6 ]----------------------------------------------------------------------------------------------------------------------------------------------------------
QUERY PLAN |   Functions: 4
-[ RECORD 7 ]----------------------------------------------------------------------------------------------------------------------------------------------------------
QUERY PLAN |   Options: Inlining true, Optimization true, Expressions true, Deforming true

【问题讨论】:

  • “几十个点”对于 PostGIS 来说并不大也不复杂——我只是简单地索引几何列,PostGIS 会自动使用边界框。
  • @IanTurton mmt=# 使用 gist(wkb_geometry) 在标题上创建索引 geometry_idx;错误:索引行需要 16368 字节,最大大小为 8191 因此需要压缩这些几何记录的大小,这就是边界框的用途。看来,仅索引无法处理对象大小。
  • 那是哪个版本的 PostGIS?我从未见过比这更大的几何形状的问题。
  • @IanTurton - mmt=# select PostGIS_Version(); postgis_version --------------------------------------- 3.1 USE_GEOS=1 USE_PROJ=1 USE_STATS=1
  • 尝试使用 spgist 代替 gist?

标签: postgresql indexing geospatial postgis spatial-index


【解决方案1】:

问题在于您将geometrygeography 混合在一起,而PostgreSQL 将geom_bounding_box 转换为geography 以便它们匹配。

现在您已经索引了geom_bounding_box,但没有索引geom_bounding_box::geography,这是不同的。

要么使用'SRID=4326;POINT(-0.145872 51.509691)'::geometry 作为第二个操作数,要么在((geom_bounding_box::geography)) 上创建GiST 索引(注意双括号)。

【讨论】:

  • 感谢 Laurenz 的回复。我已经重建了 bounding_box 和索引,并且查询现在具有匹配的类型(将点投射到几何图形),但我仍然看到完整的扫描: mmt=# explain select count(*) from heading where ST_DWithin(bounding_box, 'SRID=4326 ;POINT(-0.145872 51.509691)'::几何, 100.0);查询计划 ------------------- 完成聚合 (cost=294582.80..294582.81 rows=1 width=8) -> Gather (cost=294582.58..294582.79 rows=2宽度=8)
  • 那么PostgreSQL认为使用索引更昂贵。您可以禁用并行查询 (max_parallel_workers_per_gather = 0) 或更低的random_page_cost
  • 正如 amanin 在下面指出的那样,如果您在几何图形上运行 st_dwithin,则距离以 SRID 4326 的度数为单位,我怀疑这就是您想要的。
【解决方案2】:

编辑: 正如 mlinth 所指出的,我在下面的回答并不是真的有效。但它会带来危险:请注意给予 ST_DWithin 函数的参数,因为根据您提供地理(米)或几何(srid 单位),距离参数的单位会有所不同。


根据ST_DWithin doc,距离以SRID 单位 指定。在您的情况下,空间参考系统是地理参考系统,因此您的 100 值表示 100 度 半径,不是 100 米。这意味着大约整个世界。在这种情况下,有效地使用索引是不可能的。

如果要查找半径为 100 米的几何图形,则必须将 100 米转换为度数单位,但这取决于纬度(如果要准确的话)。

首先,我建议您使用(非常)近似的捷径:赤道处 100 米(非常)大约等于 0.001 度。所以用它替换你的距离值,如果它加快了速度(我很相信它会),那么你将能够改进你的查询以更准确。

【讨论】:

  • 如果您查看查询计划,Postgres 实际上是将 geom 转换为 geography,因此在这种情况下,参数确实是 100m(st_dwithin 的 geography 版本使用 m 中的距离)。演员是扼杀表演的原因。但是,我认为您的基本观点是正确的;要么使用具有适当投影的几何,要么使用地理并确保索引正确。
【解决方案3】:

我确实解决了这个问题,它是上述所有问题的组合,尽管不是其中任何一个。简单总结一下:

Laurenz Albe 正确地发现了地理和几何类型的混合,这很容易通过移除演员表来解决。

Ian Turton 也正确地发现了几十个点对于 gist 索引不应该是一个问题,因此我放弃了边界框近似方法并返回探索索引问题。我发现几何列是用“字节数组”(bytea)的数据类型定义的,由于“没有访问方法“spgist”的默认运算符类”,这会阻止创建 spgist 索引。这已通过更改列类型如下:

mmt=# ALTER TABLE titles
ALTER COLUMN wkb_geometry
TYPE geometry
USING wkb_geometry::geometry;

然后索引成功创建(gist 或 spgist),我已经能够并排对两者进行基准测试,发现 gist 在我的用例中稍微更有效。

Amanin 也正确地指出了根据空间参考系统的米和径向度数之间的差异。在我的一些测试中,我错误地使用了后者,但半径非常大。由于我正在使用几何类型进行索引和搜索,因此该半径值需要在径向度数上非常小才能覆盖相当大的区域。已修复!

全部放在一起,对 2600 万条记录的搜索始终在 200 毫秒到 500 毫秒内完成,偶尔会出现高达 1.1 秒的峰值。这个不错。

感谢所有提供意见、想法和讨论的人。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-12-23
    • 2019-11-12
    • 1970-01-01
    • 2012-01-19
    • 2017-10-18
    • 1970-01-01
    相关资源
    最近更新 更多