优化postgis索引查询.注意:gserialized_gist_joinsel:不支持jointype 1

mik*_*ine 2 sql postgis query-optimization postgresql-9.3

我正在运行一个查询来确定表中每个多边形的多边形中的点数.点层(twitter)有500米行,多边形层(us_urbanareas)有~3000行.点图层的几何图形已编制索引.

查询看起来像:

EXPLAIN SELECT us_urbanareas.name, us_urbanareas.gid, count(twitter.coords) AS total
FROM us_urbanareas LEFT JOIN twitter
ON st_contains(ST_Transform(us_urbanareas.geom,4326),twitter.coords)
GROUP BY us_urbanareas.gid LIMIT 500
Run Code Online (Sandbox Code Playgroud)

并解释如下:

"Limit  (cost=1265.59..47875481.71 rows=500 width=45)"
"  ->  GroupAggregate  (cost=1265.59..342780653.01 rows=3580 width=45)"
"        ->  Nested Loop Left Join  (cost=1265.59..340247956.29 rows=506532183 width=45)"
"              ->  Index Scan using us_urbanareas_pkey on us_urbanareas  (cost=0.28..1000.18 rows=3580 width=4028)"
"              ->  Bitmap Heap Scan on twitter  (cost=1265.31..94899.56 rows=14149 width=32)"
"                    Recheck Cond: (st_transform(us_urbanareas.geom, 4326) && coords)"
"                    Filter: _st_contains(st_transform(us_urbanareas.geom, 4326), coords)"
"                    ->  Bitmap Index Scan on coord_gist  (cost=0.00..1261.77 rows=42447 width=0)"
"                          Index Cond: (st_transform(us_urbanareas.geom, 4326) && coords)"
Run Code Online (Sandbox Code Playgroud)

查询有效,但速度相当慢.我收到的错误信息是:

NOTICE:  gserialized_gist_joinsel: jointype 1 not supported
Run Code Online (Sandbox Code Playgroud)

我正在处理的数据库服务器有大量内存(96gb),我们刚刚经历了一个优化其配置以充分利用它的过程.我正在运行PostGres 9.3.2

有关如何优化查询和绕过jointype的任何想法都不受支持?

更新:

更新us_urbanareas表的SRID的去除变换2.取出左连接和重新写查询按建议低于3试图打开bitmapscan =关闭

关闭bitmapscan可以大大降低解释中的成本,但会增加查询时间(使用一些随机多边形测试)两倍.

"Limit  (cost=0.83..50601686.53 rows=500 width=45)"
"  ->  GroupAggregate  (cost=0.83..362308070.41 rows=3580 width=45)"
"        ->  Nested Loop  (cost=0.83..359737718.66 rows=514063189 width=45)"
"              ->  Index Scan using us_urbanareas_pkey on us_urbanareas us  (cost=0.28..1002.76 rows=3580 width=45)"
"              ->  Index Scan using coord_gist on twitter tw  (cost=0.55..100341.53 rows=14359 width=32)"
"                    Index Cond: (us.geom && coords)"
"                    Filter: _st_contains(us.geom, coords)"
Run Code Online (Sandbox Code Playgroud)

更新4.执行

select * from pg_stat_activity;
Run Code Online (Sandbox Code Playgroud)

结果,

"INSERT INTO twitter(tweet,name,handle,location,coords,time) VALUES ($$T :($$,$$anissa aguilar$$,$$babyniss_$$,$$Abq/Rio, NM $$,ST_GeomFromText('POINT(-106.659914 35.23192)', (...)"
"SELECT version();"
"SELECT t.oid, t.xmin, t.*, relname, CASE WHEN relkind = 'r' THEN TRUE ELSE FALSE END AS parentistable,   nspname, des.description, l.lanname, p.prosrc, 
  substring(pg_get_triggerdef(t.oid), 'WHEN (.*) EXECUTE PROCEDURE') AS whenclause
  FROM pg_trigger t
 (...)"
"select * from pg_stat_activity;"
" SELECT us.name, us.gid, count(tw.coords) AS total
   FROM us_urbanareas us,  twitter tw
WHERE st_contains(us.geom, tw.coords)
   GROUP BY us.gid;"
Run Code Online (Sandbox Code Playgroud)

Joh*_*ell 5

尝试使用笛卡尔积的语法,即完全连接,在Postgres中可以简化为逗号.我知道这听起来违反直觉,但较大的表上的空间索引将考虑实际包含哪些多边形ST_Contains.我个人发现LEFT JOIN当你处理空间交叉时不自然的概念,当你有多个空间连接时,这变得更加明显,因为当你处理交叉点/遏制时,没有与左连接或右连接的自然关联在二维空间.

EXPLAIN SELECT us.name, us.gid, count(tw.coords) AS total
   FROM us_urbanareas us,  twitter tw
WHERE st_contains(ST_Transform(us.geom,4326),tw.coords)
   GROUP BY us.gid LIMIT 500;
Run Code Online (Sandbox Code Playgroud)

你可以做的另一件事可能会加快一点,就是在进行查询之前将us_urbanareas转换为4326.它可能在这么小的表上没有什么区别,但总的来说,如果你可以避免转换属于连接的字段,它将有助于优化器并减少执行操作 - 它可以产生很大的差异连接涉及两侧有许多行的表.您可以使用UpdateGeometrySrid函数执行此操作,该函数还将更新元数据视图geometry_columns.

例如,在您的情况下:

Select UpdateGeometrySrid('us_urbanareas', 'geom', 4326);
Run Code Online (Sandbox Code Playgroud)

之后,您的查询将简化为:

SELECT us.name, us.gid, count(tw.coords) AS total
   FROM us_urbanareas us,  twitter tw
WHERE st_contains(us.geom, tw.coords)
   GROUP BY us.gid LIMIT 500;
Run Code Online (Sandbox Code Playgroud)

我假设你已经看过这个,但是一些Postgis开发者之间有关于这个确切消息的交换以及左连接语法如何"混淆"Postgres查询优化器,所以即使查询运行,它也可能有一个次优计划,正如您在问题中所述.见,http://trac.osgeo.org/postgis/ticket/2342

编辑:虽然我怀疑删除ST_Transform并重写连接条件将消除错误并加快查询速度,您还可以采取其他措施来强制优化器选择某些计划.这是一种黑色艺术,通常不推荐,但空间查询有点不同,因为几何操作的复杂性往往大大超过从磁盘获取的成本 - 这实际上是解释度量单位的原因您可以尝试打开或关闭各种连接类型,例如seq_scan或hash_join,请参阅查询配置变量,例如,

SET enable_seqscan=off;
Run Code Online (Sandbox Code Playgroud)

但是,我再说一遍,这是一种黑色艺术,通常优化器"知道"最好,但我确实有通过关闭各种连接类型减少查询次数的经验.

编辑2:再次查看您的查询,我很困惑在那里需要位图heapscan.我刚刚运行了一个类似的查询,它查找一个瓷砖网格的交叉区域,包含密度计数和一些多边形,以及多边形ID的组,这非常类似.我的查询如下:

SELECT sum(ceil(st_area(st_intersection(ww.geom, dens.geom))/1000000 * osmm_add)), ww.gid 
FROM density_2010 dens, Wales_west_al2 ww 
WHERE st_intersects(dens.geom, ww.geom) GROUP BY ww.gid;
Run Code Online (Sandbox Code Playgroud)

和我的解释,

GroupAggregate  (cost=0.00..122642.68 rows=52429 width=552)
  ->  Nested Loop  (cost=0.00..108093.63 rows=52429 width=552)
       Join Filter: _st_intersects(dens.geom, ww.geom)
         ->  Index Scan using wales_west_al2_pkey on wales_west_al2 ww  (cost=0.00..6541.84 rows=124173 width=36)
         ->  Index Scan using ix_spatial_os_density on density_2010 dens  (cost=0.00..0.56 rows=1 width=516)
               Index Cond: (geom && ww.geom)
Run Code Online (Sandbox Code Playgroud)

除了缺少位图堆扫描之外,这与您的类似.你可以尝试设置:

set enable_bitmapscan=off;
Run Code Online (Sandbox Code Playgroud)

看看是否有任何区别.