Postgres 更喜欢昂贵的 ST_Intersects() 而不是便宜的索引
Postgres preferring costly ST_Intersects() over cheap index
我正在使用 Postgres 9.4 对 OSM 的完整行星转储执行一个相当简单的查询。我想要做的是获取属于德国 A8 高速公路的所有方式。在准备步骤中,我为所有行政边界关系创建了多边形并将它们存储在 table 多边形中,这样我就可以进行更简单的空间相交测试。为了实现快速查询处理,我还为 'ref' hstore 标签创建了一个索引:
CREATE INDEX idx_ways_tags_ref ON planet_20141222.ways USING btree (lower(tags->'ref'));
另外,我已经通过之前的查询得到了德国行政边界的id(结果id = 51477)
我的数据库模式是正常的 API 0.6 模式,数据是通过转储方法导入到 Postgres 中的(使用 pgsnapshot_schema_0.6*.sql 随附的脚本渗透)。还对所有 table 执行了 VACUUM ANALYZE。
有问题的查询如下所示:
SELECT DISTINCT wy.id FROM planet_20141222.ways wy, planet_20141222.polygons py
WHERE py.id=51477 AND ST_Intersects(py.geom,wy.linestring) AND ((wy.tags->'highway') is not null) AND (lower(wy.tags->'ref') like lower('A8'));
这个查询的运行时间很糟糕,因为 Postgres 更喜欢昂贵的 ST_Intersects() 测试而不是 'ref' 上的廉价(和高选择性)索引。去除交集测试时,查询returns在几毫秒内。
我该怎么做才能让 Postgres 首先评估查询中存在索引的部分,而不是在整个星球上测试与德国的交集的每条路?
我目前的解决方案是将 SQL 查询拆分为两个单独的查询。第一个进行索引支持的标签测试,第二个进行空间相交测试。我想 Postgres 可以做得更好,但是怎么做呢?
编辑:
a) OSM 0.6 导入脚本在 table:
的方式上创建以下索引
CREATE INDEX idx_ways_bbox ON ways USING gist (bbox);
CREATE INDEX idx_ways_linestring ON ways USING gist (linestring);
b) 此外,我在多边形上创建了另一个索引:
CREATE INDEX polygons_geom_tags on polygons using gist(geom, tags);
c) 查询 without ST_Intersects() 的 EXPLAIN ANALYZE 输出如下所示:
"Index Scan using ways_tags_ref on ways (cost=0.57..4767.61 rows=1268 width=467) (actual time=0.064..0.267 rows=60 loops=1)"
" Index Cond: (lower((tags -> 'ref'::text)) = 'a8'::text)"
" Filter: (((tags -> 'highway'::text) IS NOT NULL) AND (lower((tags -> 'ref'::text)) ~~ 'a8'::text))"
" Rows Removed by Filter: 5"
"Total runtime: 0.300 ms"
使用ST_Intersects()的查询运行时间超过15分钟,所以我取消了。
也许试试这样的东西..?
WITH wy AS (
SELECT * FROM planet_20141222.ways
WHERE ((tags->'highway') IS NOT null)
AND (lower(tags->'ref') LIKE lower('A8'))
)
SELECT DISTINCT wy.id
FROM wy, planet_20141222.polygons py
WHERE py.id=51477
AND ST_Intersects(py.geom,wy.linestring);
我正在使用 Postgres 9.4 对 OSM 的完整行星转储执行一个相当简单的查询。我想要做的是获取属于德国 A8 高速公路的所有方式。在准备步骤中,我为所有行政边界关系创建了多边形并将它们存储在 table 多边形中,这样我就可以进行更简单的空间相交测试。为了实现快速查询处理,我还为 'ref' hstore 标签创建了一个索引:
CREATE INDEX idx_ways_tags_ref ON planet_20141222.ways USING btree (lower(tags->'ref'));
另外,我已经通过之前的查询得到了德国行政边界的id(结果id = 51477)
我的数据库模式是正常的 API 0.6 模式,数据是通过转储方法导入到 Postgres 中的(使用 pgsnapshot_schema_0.6*.sql 随附的脚本渗透)。还对所有 table 执行了 VACUUM ANALYZE。
有问题的查询如下所示:
SELECT DISTINCT wy.id FROM planet_20141222.ways wy, planet_20141222.polygons py
WHERE py.id=51477 AND ST_Intersects(py.geom,wy.linestring) AND ((wy.tags->'highway') is not null) AND (lower(wy.tags->'ref') like lower('A8'));
这个查询的运行时间很糟糕,因为 Postgres 更喜欢昂贵的 ST_Intersects() 测试而不是 'ref' 上的廉价(和高选择性)索引。去除交集测试时,查询returns在几毫秒内。
我该怎么做才能让 Postgres 首先评估查询中存在索引的部分,而不是在整个星球上测试与德国的交集的每条路?
我目前的解决方案是将 SQL 查询拆分为两个单独的查询。第一个进行索引支持的标签测试,第二个进行空间相交测试。我想 Postgres 可以做得更好,但是怎么做呢?
编辑:
a) OSM 0.6 导入脚本在 table:
的方式上创建以下索引CREATE INDEX idx_ways_bbox ON ways USING gist (bbox);
CREATE INDEX idx_ways_linestring ON ways USING gist (linestring);
b) 此外,我在多边形上创建了另一个索引:
CREATE INDEX polygons_geom_tags on polygons using gist(geom, tags);
c) 查询 without ST_Intersects() 的 EXPLAIN ANALYZE 输出如下所示:
"Index Scan using ways_tags_ref on ways (cost=0.57..4767.61 rows=1268 width=467) (actual time=0.064..0.267 rows=60 loops=1)"
" Index Cond: (lower((tags -> 'ref'::text)) = 'a8'::text)"
" Filter: (((tags -> 'highway'::text) IS NOT NULL) AND (lower((tags -> 'ref'::text)) ~~ 'a8'::text))"
" Rows Removed by Filter: 5"
"Total runtime: 0.300 ms"
使用ST_Intersects()的查询运行时间超过15分钟,所以我取消了。
也许试试这样的东西..?
WITH wy AS (
SELECT * FROM planet_20141222.ways
WHERE ((tags->'highway') IS NOT null)
AND (lower(tags->'ref') LIKE lower('A8'))
)
SELECT DISTINCT wy.id
FROM wy, planet_20141222.polygons py
WHERE py.id=51477
AND ST_Intersects(py.geom,wy.linestring);