【问题标题】:Query by coordinates takes too long - options to optimize?按坐标查询需要太长时间 - 优化选项?
【发布时间】:2014-08-04 10:16:58
【问题描述】:

我有一个存储事件的表(目前大约 5M,但还会更多)。每个事件都有两个我关心的查询属性——location(纬度和经度对)和relevancy

我的目标是:对于给定的位置范围(SW / NE 纬度/经度对,因此 4 个浮点数)返回位于这些范围内的 relevancy 的前 100 个事件。

我目前正在使用以下查询:

select * 
from event 
where latitude >= :swLatitude 
and latitude <= :neLatitude 
and longitude >= :swLongitude 
and longitude <= :neLongitude 
order by relevancy desc 
limit 100

让我们暂时搁置此查询无法处理的日期线环绕问题。

这适用于较小的位置范围,但每当我尝试使用较大的位置范围时,就会出现相当严重的滞后。

我定义了以下索引:

CREATE INDEX latitude_longitude_relevancy_index
  ON event
  USING btree
  (latitude, longitude, relevancy);

表格本身非常简单:

CREATE TABLE event
(
  id uuid NOT NULL,
  relevancy double precision NOT NULL,
  data text,
  latitude double precision NOT NULL,
  longitude double precision NOT NULL
  CONSTRAINT event_pkey PRIMARY KEY (id)
)

我尝试了explain analyze 并得到了以下结果,我认为这意味着甚至没有使用索引:

"Limit  (cost=1045499.02..1045499.27 rows=100 width=1249) (actual time=14842.560..14842.575 rows=100 loops=1)"
"  ->  Sort  (cost=1045499.02..1050710.90 rows=2084754 width=1249) (actual time=14842.557..14842.562 rows=100 loops=1)"
"        Sort Key: relevancy"
"        Sort Method: top-N heapsort  Memory: 351kB"
"        ->  Seq Scan on event  (cost=0.00..965821.22 rows=2084754 width=1249) (actual time=3090.660..12525.695 rows=1983213 loops=1)"
"              Filter: ((latitude >= 0::double precision) AND (latitude <= 180::double precision) AND (longitude >= 0::double precision) AND (longitude <= 180::double precision))"
"              Rows Removed by Filter: 3334584"
"Total runtime: 14866.532 ms"

我在 Win7 上使用 PostgreSQL 9.3,对于这个看似简单的任务,迁移到其他任何东西似乎有点矫枉过正。

问题:

  • 有什么方法可以使用不同的索引来帮助当前查询更快?
  • 有什么方法可以更快地重写当前查询?
  • 最简单的方法是什么?安装 PostGIS 并使用 GEOGRAPHYdata 类型?这真的会给我现在正在做的事情带来性能优势吗?哪个 PostGIS 函数最适合此查询?

编辑 #1:vacuum full analyze 的结果:

INFO:  vacuuming "public.event"
INFO:  "event": found 0 removable, 5397347 nonremovable row versions in 872213 pages
DETAIL:  0 dead row versions cannot be removed yet.
CPU 17.73s/11.84u sec elapsed 154.24 sec.
INFO:  analyzing "public.event"
INFO:  "event": scanned 30000 of 872213 pages, containing 185640 live rows and 0 dead     rows; 30000 rows in sample, 5397344 estimated total rows
Total query runtime: 360092 ms.

抽真空后的结果:

"Limit  (cost=1058294.92..1058295.17 rows=100 width=1216) (actual time=6784.111..6784.121 rows=100 loops=1)"
"  ->  Sort  (cost=1058294.92..1063405.89 rows=2044388 width=1216) (actual time=6784.109..6784.113 rows=100 loops=1)"
"        Sort Key: relevancy"
"        Sort Method: top-N heapsort  Memory: 203kB"
"        ->  Seq Scan on event  (cost=0.00..980159.88 rows=2044388 width=1216) (actual time=0.043..6412.570 rows=1983213 loops=1)"
"              Filter: ((latitude >= 0::double precision) AND (latitude <= 180::double precision) AND (longitude >= 0::double precision) AND (longitude <= 180::double precision))"
"              Rows Removed by Filter: 3414134"
"Total runtime: 6784.170 ms"

【问题讨论】:

  • 我同意索引没有被使用。索引 ddl 和查询对我来说看起来不错。您可以尝试更新表格上的统计信息吗?这个成本cost=0.00..965821.22 在我看来非常不准确。也许您只是在运行查询之前将数据转储到表中。
  • 您使用的是哪个 Postgres 版本?我看到对带有uuid 列的表进行seq 扫描有时会相当慢。您可以尝试使用integer 列作为 ID,看看是否有任何改变?我猜未使用索引的原因是where 条件仍返回近 200 万行(共 530 万行)。如果条件返回的行数少于大约 15%,Postgres 通常只会使用索引。
  • Andreas,我在编辑 #1 中做了一个vacuum full analyze 并在上面报告了结果。
  • @a_horse_with_no_name,我在 Windows 7 64 位上使用 Postgres 9.3。我不确定我是否热衷于迁移到整数主键 - 在任何情况下,seq 扫描都会太慢,即使在使用整数时它会变得更快。返回太多行的想法很有趣 - 但我能做什么?

标签: sql postgresql indexing postgis


【解决方案1】:

使用空间索引会更好这种查询有两个单独的纬度、经度值。不过,您需要先创建一个几何类型,然后索引并在查询中使用它,而不是当前使用的单独的纬度/经度对。

下面将创建一个几何类型,填充它,并为其添加一个索引,确保它是一个点并且在纬度/经度,称为 EPSG:4326

alter table event add column geom geometry(POINT, 4326);
update event set geom=ST_SetSrid(ST_MakePoint(lon, lat), 4326);
create index ix_spatial_event_geom on event using gist(geom);

然后您可以运行以下查询来获取您的事件,这将使用空间相交,这应该利用您的空间索引:

Select * from events where ST_Intersects(ST_SetSRID(ST_MakeBox2D(ST_MakePoint(swLon, swLat), 
    ST_MakePoint(neLon, neLat)),4326), geom) 
order by relevancy desc limit 100;

您可以使用带有两组点的 ST_MakeBOX2D 为您的交叉点创建边界框,这些点将位于边界框的对角,因此 SW 和 NE 或 NW 和 SE 对都可以工作。

当您对此运行解释时,您应该会发现空间索引已包含在内。这将比 lon 和 lat 列上的两个单独的索引执行得好得多,因为您只命中一个为空间搜索优化的索引,而不是两个 B 树。我意识到这代表了另一种方式,除了间接地之外,它不会回答你原来的问题。

编辑: Mike T 提出了非常好的观点,即对于 4326 中的边界框搜索,使用几何数据类型更合适、更快捷,并且 && 运算符作为 SRID 将被忽略无论如何,例如,

 where ST_MakeBox2D(ST_MakePoint(swLon, swLat), ST_MakePoint(neLon, neLat)) && geom

【讨论】:

  • 这看起来很有希望,谢谢(实际上回答了我列出的第三个要点——我怀疑这是正确的方法)。我会回家测试一下。但是一个快速的问题 - 为什么使用Geometry 类型而不是Geography 类型?我读了gis.stackexchange.com/questions/6681/…,但我仍然不确定哪个最适合我的用例。理论上数据似乎更适合Geography
  • 查看这里:postgis.net/docs/manual-1.5/… 进行快速比较。一般来说,几何类型有更多可用的功能,但如果你有跨越全球的数据,地理会更好,因为它会处理日期线之类的事情。我不确定您的确切用例是什么,但 ST_Intersects 将适用于几何或地理。您总是可以对另一个进行测试:D
  • 谢谢,初步测试表明这很有帮助(不过映射到 Spring JPA 会很困难)。我仍然不清楚哪些索引在这里最有效——我在relevancy desc 上做了一个,在gist(location) 上做了另一个,但是我应该为两列都做两列索引吗?如果是,那么两者都是@987654332 @ 和 gist(location),relevancy desc 还是这些选项之一?这是假设我需要“窄”和“宽”查询。我想这一切都应该通过测试来回答,尽管也许有你经验的人可以立即说出来。
  • 是的,我认为这会很有帮助。不过,我对 Spring JPA 无能为力:-( 一般来说,Postgres 查询优化器在我的经验中做得非常好。我认为空间索引会比相关性更快地缩小你的结果。什么解释说现在有了 ST_Intersects 查询?
  • 如果只需要bbox重叠,&amp;&amp; operator是最直接的,ST_Intersects(geometry, geometry)间接使用。 IE。 where ST_MakeBox2D(ST_MakePoint(swLon, swLat), ST_MakePoint(neLon, neLat)) &amp;&amp; geom(此运算符忽略 SRID)。
猜你喜欢
  • 2023-03-12
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2022-07-19
  • 1970-01-01
  • 2021-10-13
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多