暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

[译文] PostGIS的(许多)空间索引

原创 PAUL RAMSEY 2021-08-09
2758

PostGIS中使用空间索引来快速搜索空间中的对象。实际上,这意味着非常快速地回答以下形式的问题:

  • “这里面的所有东西”或
  • “这件东西附近的所有东西”

由于空间对象通常非常大且复杂(例如,海岸线通常由数千个点定义),因此空间索引使用“边界框”作为索引和搜索键:

  • 边界框很小,固定大小,2D 框只有 4 个浮点数;和,
  • 与测试诸如遏制之​​类的东西相比,边界框非常便宜。

所以空间索引的简单故事是:如果您计划进行空间查询(如果您正在存储空间对象,您可能会这样做),您应该为您的表创建一个空间索引。

创建空间索引非常简单:

CREATE INDEX mytable_geom_x ON mytable USING GIST (geom);

这将在几何列上创建一个 GIST 索引,使用几何的默认“运算符类”。故事结局?差远了。

这么多索引

仅对于PostGIS 几何类型,实际上就有10 个不同的运算符类!

SELECT opcname, amname FROM pg_opclass oc JOIN pg_am am ON (am.oid = oc.opcmethod) JOIN pg_type typ ON (oc.opcintype = typ.oid) WHERE typ.typname = 'geometry';
opcname | amname --------------------------------+-------- btree_geometry_ops | btree hash_geometry_ops | hash gist_geometry_ops_2d | gist gist_geometry_ops_nd | gist brin_geometry_inclusion_ops_2d | brin brin_geometry_inclusion_ops_3d | brin brin_geometry_inclusion_ops_4d | brin spgist_geometry_ops_2d | spgist spgist_geometry_ops_3d | spgist spgist_geometry_ops_nd | spgist

前两个运算符类,“btree”和“hash”索引,可以安全地忽略。最终用户无需创建这些索引,它们的存在只是为了启用某些核心数据库功能。

  • 对于在查询中支持“ORDER BY”的任何类型,PostgreSQL 需要一个btree运算符类,并且几何类型是 sortable。
  • 对于在查询中支持“DISTINCT”的任何类型,PostgreSQL 都需要一个哈希运算符类,就像几何一样。

其余索引都支持空间索引的核心特征:查找查询空间对象的边界框所包含的所有空间对象。

正如我们在列表中看到的,实现在用于提供功能(GIST、SPGIST 和 BRIN)的底层数据库访问方法和进行搜索的维度空间(2D、3D、4D)方面有所不同。

GIST vs SPGIST vs BRIN

GIST 访问方法是用于构建索引的通用 API ——“通用搜索树”是首字母缩略词所代表的意思 —— 用于几何类型的索引方案实现了R 树结构。R-Tree 支持范围广泛的不同空间用例和数据模式。高度重叠的数据,以及布局规则的数据。具有广泛可变空间范围的数据以及具有统一大小的数据。

image.png

SP-GIST 访问方法也是一种通用方案,但专门针对“空间分区”进行了调整。也就是说,可以将索引节点拆分为两个完全不重叠的域的方案。用于几何类型的方案是四叉树的变体,在每次拆分时将每个节点划分为四个不重叠的子节点。

image.png

BRIN 访问方法(“块范围索引”)在完全不同的假设下运行。列的 BRIN 索引存储该列涵盖的每个数据库页面的范围。BRIN 索引是一个范围列表,将搜索空间的值映射到数据库表中的数据页。

结果是一个非常小的 BRIN 索引,即使对于非常大的表也是如此。但是,索引的有效性与数据的基本排序成正比。一个充满流时间戳数据的表,其中写入了新值但没有删除任何内容,将是时间戳列上 BRIN 索引的绝佳候选者,因为数据将按时间整齐排序。

在空间域中,按几何对数据进行预排序可以为 BRIN 生成一个非常“可搜索”的数据集,这就是我们在这篇文章中比较 BRIN 性能时对数据所做的。

比较索引构建

为了提供一些非常基本的性能比较,我们将查看 1M 随机点的测试数据集。

CREATE TABLE points AS SELECT ST_MakePoint( 1000*random(), 1000*random(), 1000*random())::geometry(PointZ) AS geom, pk FROM generate_series(1,1000000) pk; ALTER TABLE points ADD PRIMARY KEY (pk);

image.png
要为 BRIN 测试生成数据,我们将做同样的事情,但添加一个“ORDER BY geom”子句以按位置对数据进行预排序。

现在我们可以使用每种类型构建索引,并检查构建时间和大小。

Build Size GIST 15.0s 53 MB SPGIST 5.6s 44 MB BRIN 0.4s 24 KB

构建时间和索引大小的一些实质性差异!正如预期的那样,BRIN 索引构建速度快且非常小,但正如我们稍后将看到的那样,这是有代价的。与 SPGIST 相比,更通用的 GIST 索引在构建时间和大小方面都会受到惩罚。

比较查询时间

为了比较查询时间,我们使用了一个查询,该查询在数据区域上构建 1000 个随机框,然后使用空间索引运算符“&&”将这些框连接到数据表。

WITH xy AS ( SELECT 1000*random() AS x, 1000*random() AS y FROM generate_series(1, 1000)), boxes AS ( SELECT ST_MakeEnvelope(x-5, y-5, x+5, y+5) AS geom FROM xy ) SELECT Count(*) FROM points, boxes WHERE points.geom && boxes.geom;

通过对这个查询进行计时,我们可以了解索引性能,同时最大限度地减少查询启动开销对结果的影响。

Time GIST 230ms SPGIST 150ms BRIN 21810ms

在这里,我们看到了 BRIN 指数的下行空间。每次查询运行涉及 1000 次 box-vs-data 索引调用,因此 GIST 和 SPGIST 索引的平均每次调用远低于1ms。

另一方面,BRIN每次调用的平均时间约为 20毫秒。如果您只需要拨打一个电话,那么对于您的用例来说就足够了。然而,它指出了 BRIN 索引的特殊用途性质:对于大表,数据按与查询结构匹配的顺序顺序放置。

SPGIST 赢了?

到目前为止,新的 SPGIST 索引似乎超过了旧的 GIST 基础设施,但重要的是要记住 SPGIST 最强的地方:数据不重叠的地方,以及数据可以完美地划分为空间的地方。我们的统一随机点表是 SPGIST 表现优异的完美数据。

更公平的测试可能会使用更多真实世界的数据。这是不列颠哥伦比亚省 9349 个多边形的表格。它们不重叠(对 SPGIST 来说非常好)但大小不均匀。
image.png

仅测试 GIST 和 SPGIST,使用另一个包含 10,000 个框的随机框查询,我们得到以下结果:

Time GIST 270ms SPGIST 375ms

一般来说,当数据有很多重叠时,GIST 会优于 SPGIST。当重叠较少时,SPGIST 将优于 GIST。

从视觉上看,使用这种具有高度叠加的随机大小圆圈的合成数据集,GIST 的速度提高了约 5-15%。

image.png

在使用这个由重叠较少的随机大小圆组成的合成数据集时,SPGIST 的速度提高了约 5-15%。

image.png

结论

  • 空间数据需要一个空间索引!
  • 不要忽视在久经考验的 GIST 上使用较新的 SPGIST 实现的可能性,特别是如果数据相对统一并且没有很多重叠。
  • 仅当数据表很大并且以高度空间相关的顺序存储时才考虑 BRIN。

原文地址:https://blog.crunchydata.com/blog/the-many-spatial-indexes-of-postgis?xzs

「喜欢这篇文章,您的关注和赞赏是给作者最好的鼓励」
关注作者
【版权声明】本文为墨天轮用户原创内容,转载时必须标注文章的来源(墨天轮),文章链接,文章作者等基本信息,否则作者和墨天轮有权追究责任。如果您发现墨天轮中有涉嫌抄袭或者侵权的内容,欢迎发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

评论