当前版本的PostgreSQL(包括9.6)在TOAST表后面的查询优化器中存在弱点。TOAST 表是一种“扩展室”,用于存储不适合普通数据页面(例如长文本、图像或具有许多顶点的复杂几何图形)的巨大值(就数据大小而言)。 有关更多信息,请参阅 Toast的PostgreSQL文档。
如果您碰巧有一个具有相当大的几何形状的表格,但它们的行数不是太多(例如包含所有欧洲国家的高分辨率边界的表格),则会出现问题。 那么表本身很小,但是它使用了大量的TOAST空间。 在我们的示例中,表本身约有 80 行,仅使用 3 个数据页,但 TOAST 表使用 8225 个页面。
现在,让我们使用几何运算符&&来查询很少匹配的边界框。对于查询优化器,该表似乎只有 3 页和 80 行。 优化程序估计按顺序遍历小表比使用索引更快。然后我们决定忽略 GiST 索引。 通常,这种做法是正确的。 但是,在这种情况下,&&运算符必须从磁盘调用所有几何图形并将其与边界框进行比较,因此还必须调用所有TOAST页面。
要查看您是否遇到此问题,请使用“EXPLAIN ANALYZE”postgresql 命令。 有关更多信息和技术细节,您可以阅读 PostgreSQL 性能邮件列表上的线程:http://archives.postgresql.org/pgsql-performance/2005-02/msg00030.php
另请参阅 PostGIS 中的新线程 https://lists.osgeo.org/pipermail/postgis-devel/2017-June/026209.html
PostgreSQL社区正试图通过制作 TOAST 感知查询引用来解决这个问题。目前,有两种解决办法:
第一个解决方法是强制查询规划器使用索引。发送“SET enable_seqscan TO off;” 在发出查询之前发送到服务器。这基本上迫使查询规划器尽可能避免顺序扫描。所以它像往常一样使用 GIST 索引。 但是必须在每个连接上设置此标志,并且它会导致查询规划器在其他情况下做出错误估计,因此您应该在查询后“SET enable_seqscan TO on;” 。
第二个解决方法是使顺序扫描与查询规划器认为的一样快。这可以通过创建一个“缓存”bbox的附加列并与之匹配来实现。在我们的示例中,命令如下:
SELECT AddGeometryColumn('myschema','mytable','bbox','4326','GEOMETRY','2'); UPDATE mytable SET bbox = ST_Envelope(ST_Force2D(geom));
现在更改查询以对bbox使用&&运算符 而不是geom_column,例如:
SELECT geom_column FROM mytable WHERE bbox && ST_SetSRID('BOX3D(0 0,1 1)'::box3d,4326);
当然,如果您更改或添加行到mytable,则必须保持 bbox“同步”。最透明的方法是使用触发器,但您也可以修改应用程序以保持 bbox 列最新或在每次修改后运行上面的 UPDATE 查询。
对于大多数只读的表,以及大多数查询使用单个索引的情况,PostgreSQL 提供了 CLUSTER 命令。该命令按照与索引标准相同的顺序对所有数据行进行物理重新排序,从而产生两个性能优势:首先,对于索引范围扫描,数据表上的查找次数大大减少。其次,如果您的工作集集中在索引上的一些小间隔,则您的缓存会更有效,因为数据行分布在更少的数据页上。(此时请阅读 PostgreSQL 手册中的 CLUSTER 命令文档。)
但是,由于 GiST 索引只是忽略 NULL 值,因此当前无法在 PostGIS 中对 GiST 索引进行聚类,并且您会收到以下错误消息:
lwgeom=# CLUSTER my_geom_index ON my_table; ERROR: cannot cluster when index access method does not handle null values HINT: You may be able to work around this by marking column "geom" NOT NULL.
正如提示消息所说,向表添加“非空”限制将暂时解决此缺陷。 例如:
lwgeom=# ALTER TABLE my_table ALTER COLUMN geom SET not null; ALTER TABLE
当然,如果您实际上需要几何列中的 NULL 值,则这将不起作用。 此外,您必须使用上述方法添加约束,使用 CHECK 约束,例如“ALTER TABLE blubb ADD CHECK (geometry is not null);” 不管用。
有时,您的表中碰巧有3D 或 4D 数据,但始终使用仅输出 2D 几何图形的符合OpenGIS 的 ST_AsText() 或 ST_AsBinary() 函数来访问它。他们通过内部调用 ST_Force2D() 函数来实现这一点,这会给大型几何图形带来巨大的开销。 为了避免这种开销,一次性预先删除这些额外的维度可能是可行的:
UPDATE mytable SET geom = ST_Force2D(geom); VACUUM FULL ANALYZE mytable;
请注意,如果您使用 AddGeometryColumn() 添加几何列,则几何维度将会受到限制。要绕过它,您需要删除约束。请记住更新geometry_columns 表中的条目并随后重新创建约束。
对于大型表,明智的做法是通过 WHERE 子句和主键或其他可行标准将 UPDATE 限制为表的一部分,然后运行简单的“VACUUM;”,从而将此 UPDATE 划分为更小的部分。 在您的更新之间。 这大大减少了对临时磁盘空间的需求。此外,如果您有混合维度几何图形,则通过“WHEREdimension(geom)>2”限制 UPDATE 会跳过对已经处于 2D 中的几何图形的重写。