优化postgresql查询

优化postgresql查询,sql,postgresql,query-optimization,Sql,Postgresql,Query Optimization,我在PostgreSQL 9.1-flight_2012_09_12中有两个表,包含约500000行,position_2012_09_12包含约550万行。我正在运行一个简单的连接查询,它需要很长时间才能完成,尽管表并不小,但我相信在执行过程中会有一些重大收获 查询是: SELECT f.departure, f.arrival, p.callsign, p.flightkey, p.time, p.lat, p.lon, p.altitude_ft, p.speed FRO

我在PostgreSQL 9.1-flight_2012_09_12中有两个表,包含约500000行,position_2012_09_12包含约550万行。我正在运行一个简单的连接查询,它需要很长时间才能完成,尽管表并不小,但我相信在执行过程中会有一些重大收获

查询是:

SELECT f.departure, f.arrival, 
       p.callsign, p.flightkey, p.time, p.lat, p.lon, p.altitude_ft, p.speed 
FROM position_2012_09_12 AS p 
JOIN flight_2012_09_12 AS f 
     ON p.flightkey = f.flightkey 
WHERE p.lon < 0 
      AND p.time BETWEEN '2012-9-12 0:0:0' AND '2012-9-12 23:0:0'

我想不出任何其他方法来重写查询,所以我在这一点上被难住了。如果当前的设置是一样好,那么就这样吧,但在我看来,它应该比现在快得多。任何帮助都将不胜感激。

行数估计值非常合理,因此我怀疑这是一个统计问题

我会尝试:

  • 如果您经常搜索
    lon<0
    ,则在
    position\u 2012\u 09\u 12(lon,“time”)
    上创建索引,或者在
    position\u 2012\u 09\u 12(“time”)上创建部分索引,其中(lon<0)

  • 设置
    random\u page\u成本
    更低,可能为1.1。看看(a)这是否会改变计划,以及(b)新计划是否真的更快。为了测试避免seqscan是否更快,您可以
    设置enable_seqscan=off
    ;如果是,请更改成本参数

  • 增加此查询的
    work\u mem
    <代码>在运行前设置工时\u mem=10M
    或其他

  • 正在运行最新的PostgreSQL(如果尚未运行)。始终在问题中指定您的PostgreSQL版本。(编辑后更新):您使用的是9.1;那很好。9.2中最大的性能改进是只进行索引扫描,而且您似乎不太可能从这个查询的只进行索引扫描中获益匪浅


如果可以去掉列来缩小行的范围,那么性能也会有所提高。它不会有太大的区别,但会有一些区别。

您获得顺序扫描的原因是Postgres认为,与使用索引相比,它可以读取更少的磁盘页面。这可能是对的。考虑一下,如果使用非覆盖索引,则需要读取所有匹配索引页。它本质上输出一个行标识符列表。然后,DB引擎需要读取每个匹配的数据页

您的位置表每行使用71个字节,加上geom类型所需的任何内容(我假设16个字节用于说明),总共87个字节。Postgres页面的大小为8192字节。因此,每页大约有90行

您的查询匹配5563070行中的3950815行,约占总数的70%。假设数据是随机分布的,就where过滤器而言,找到没有匹配行的数据页的几率几乎为30%^90。这基本上没什么。因此,无论索引有多好,您仍然必须读取所有数据页。如果您无论如何都要阅读所有页面,那么表扫描通常是一种很好的方法

这里要说的是,我说的是非覆盖指数。如果您准备创建索引来回答查询本身的问题,那么就可以完全避免查找数据页,这样您就回到了游戏中。我认为以下几点值得一看:

flight_2012_09_12 (flightkey, departure, arrival)
position_2012_09_12 (filghtkey, time, lon, ...)
position_2012_09_12 (lon, time, flightkey, ...)
position_2012_09_12 (time, long, flightkey, ...)
此处的点表示所选列的其余部分。你只需要一个位置指数,但很难说哪一个是最好的。第一种方法可能允许对预排序的数据进行合并联接,代价是读取整个第二个索引来进行过滤。第二个和第三个允许对数据进行预筛选,但需要哈希联接。考虑到散列连接中的开销,合并连接可能是一个不错的选择

由于您的查询每行需要87个字节中的52个字节,并且索引有开销,因此索引占用的空间可能不会比表本身少很多(如果有的话)


另一种方法是通过观察集群来攻击其“随机分布”的一面。

您能提供public.position\u 2012\u 09\u 12表lon和时间列的统计信息吗?也许某个时间你在使用哪个版本的Postgresql?在进行查询之前你分析过你的表吗?估计的行数和实际的行数看起来很匹配,所以我怀疑这些表是否需要分析。哈希连接上的512个批看起来很大,而1024kb的内存使用量看起来很小——我想知道如果使用更大的work\u mem,它是否会做得更好。除此之外,这个计划在我看来很好,性能可能只会随着硬件的改进而提高;始终(几乎)使用独占上界(
在我看来,在飞行表上添加覆盖索引并不值得,因为完整扫描似乎只需要220毫秒?@davidadridge Fair point,尽管在两个表上都以飞行键开头的覆盖索引可以允许合并联接,我希望这比预排序数据上的哈希联接更快。@davidadridge The用户使用的是PostgreSQL 9.1,它没有索引扫描(如覆盖索引),所以这个问题还是没有意义的。@CraigRinger哦,这就是MVCC的问题,是吗?如果是的话,这在9.2中有什么不同吗?@Davidardridge你指的是索引中没有可见性信息的方式,所以Pg必须检查堆以确定行是否可见-这就是你所说的“MVCC问题”吗?如果是这样,9.2添加了仅索引扫描,可通过使用可见性映射避免大多数堆访问;请参阅发行说明和
               Table "public.flight_2012_09_12"
   Column       |            Type             | Modifiers 
--------------------+-----------------------------+-----------
callsign           | character varying(8)        | 
flightkey          | integer                     | 
source             | character varying(16)       | 
departure          | character varying(4)        | 
arrival            | character varying(4)        | 
original_etd       | timestamp without time zone | 
original_eta       | timestamp without time zone | 
enroute            | boolean                     | 
etd                | timestamp without time zone | 
eta                | timestamp without time zone | 
equipment          | character varying(6)        | 
diverted           | timestamp without time zone | 
time               | timestamp without time zone | 
lat                | double precision            | 
lon                | double precision            | 
altitude           | character varying(7)        | 
altitude_ft        | integer                     | 
speed              | character varying(4)        | 
asdi_acid          | character varying(4)        | 
enroute_eta        | timestamp without time zone | 
enroute_eta_source | character varying(1)        | 
Indexes:
"flight_2012_09_12_flightkey_idx" btree (flightkey)
"idx_2012_09_12_altitude_ft" btree (altitude_ft)
"idx_2012_09_12_arrival" btree (arrival)
"idx_2012_09_12_callsign" btree (callsign)
"idx_2012_09_12_departure" btree (departure)
"idx_2012_09_12_diverted" btree (diverted)
"idx_2012_09_12_enroute_eta" btree (enroute_eta)
"idx_2012_09_12_equipment" btree (equipment)
"idx_2012_09_12_etd" btree (etd)
"idx_2012_09_12_lat" btree (lat)
"idx_2012_09_12_lon" btree (lon)
"idx_2012_09_12_original_eta" btree (original_eta)
"idx_2012_09_12_original_etd" btree (original_etd)
"idx_2012_09_12_speed" btree (speed)
"idx_2012_09_12_time" btree ("time")

          Table "public.position_2012_09_12"
Column    |            Type             | Modifiers 
-------------+-----------------------------+-----------
 callsign    | character varying(8)        | 
 flightkey   | integer                     | 
 time        | timestamp without time zone | 
 lat         | double precision            | 
 lon         | double precision            | 
 altitude    | character varying(7)        | 
 altitude_ft | integer                     | 
 course      | integer                     | 
 speed       | character varying(4)        | 
 trackerkey  | integer                     | 
 the_geom    | geometry                    | 
Indexes:
"index_2012_09_12_altitude_ft" btree (altitude_ft)
"index_2012_09_12_callsign" btree (callsign)
"index_2012_09_12_course" btree (course)
"index_2012_09_12_flightkey" btree (flightkey)
"index_2012_09_12_speed" btree (speed)
"index_2012_09_12_time" btree ("time")
"position_2012_09_12_flightkey_idx" btree (flightkey)
"test_index" btree (lon)
"test_index_lat" btree (lat)
flight_2012_09_12 (flightkey, departure, arrival)
position_2012_09_12 (filghtkey, time, lon, ...)
position_2012_09_12 (lon, time, flightkey, ...)
position_2012_09_12 (time, long, flightkey, ...)