在where子句中使用非常常见的值时查询速度慢

时间:2017-12-19 17:42:37

标签: postgresql query-performance

我有一些航班(约3万)和一些汇总航班(约15万),现在我想要在aggregated_flights中不存在的航班。

现在,我想知道如何在此查询中获得最佳效果:

select 
  f.id 
from 
  flights f left join 
  aggregated_flights af on af.flight_id = f.id 
where 
  af.flight_id is null and 
  f.status = 'COMMITED' 
;

如果我省略了status子句,那么查询速度非常快,但是当我包含它时,查询需要1-2分钟。

状态列中的值为' COMMITED'约99%的航班

我已经创建了这样的部分索引:

create index on flights (id) where status = 'COMMITED';

但似乎没有效果 - 查询仍然很慢。

这里的建议是什么?

(在Postgresql 9.4和9.6中有经验)

表格定义:

app=> \d flights
                                          Table "public.flights"
        Column        |            Type             |                      Modifiers                       
----------------------+-----------------------------+------------------------------------------------------
 id                   | integer                     | not null default nextval('flights_id_seq'::regclass)
 name                 | character varying           | 
 aircraft_id          | integer                     | 
 status               | character varying           | 
 departure_airport_id | integer                     | 
 arrival_airport_id   | integer                     | 
 departure_time       | timestamp without time zone | 
 off_block            | timestamp without time zone | 
 arrival_time         | timestamp without time zone | 
 on_block             | timestamp without time zone | 
 radiation_amount     | numeric(10,6)               | 
 total_day_minutes    | integer                     | 
 total_night_minutes  | integer                     | 
 total_instr_minutes  | integer                     | 
 approach_type_id     | integer                     | 
 note                 | character varying           | 
 created_at           | timestamp without time zone | 
 updated_at           | timestamp without time zone | 
 flight_type_id       | integer                     | 
 owner_id             | integer                     | 
 night_landing        | boolean                     | 
 load_filename        | character varying           | 
 recalc               | boolean                     | 
Indexes:
    "flights_pkey" PRIMARY KEY, btree (id)
    "flights_id_idx" btree (id) WHERE status::text = 'COMMITED'::text
    "index_flights_combined" btree (name, departure_airport_id, off_block)
    "index_flights_on_aircraft_id" btree (aircraft_id)
    "index_flights_on_approach_type_id" btree (approach_type_id)
    "index_flights_on_arrival_airport_id" btree (arrival_airport_id)
    "index_flights_on_created_at" btree (created_at)
    "index_flights_on_departure_airport_id" btree (departure_airport_id)
    "index_flights_on_flight_type_id" btree (flight_type_id)
    "index_flights_on_off_block" btree (off_block)
    "index_flights_on_on_block" btree (on_block)
    "index_flights_on_owner_id" btree (owner_id)

自动清理:

app=> show autovacuum;
 autovacuum 
------------
 on
(1 row)

分析:

app=> analyze verbose flights;
INFO:  analyzing "public.flights"
INFO:  "flights": scanned 30000 of 80606 pages, containing 1161009 live rows and 0 dead rows; 30000 rows in sample, 3122535 estimated total rows
ANALYZE

解释输出:

app=> explain (analyze, buffers) select f.id from flights f left join aggregated_flights af on af.flight_id = f.id where af.flight_id is null and f.status = 'COMMITED' limit 100;

Limit  (cost=7.25..68.59 rows=100 width=4) (actual time=58744.490..58744.604 rows=100 loops=1)
  Buffers: shared hit=367361 read=248982
  ->  Merge Anti Join  (cost=7.25..1829081.46 rows=2981880 width=4) (actual time=58744.489..58744.586 rows=100 loops=1)
        Merge Cond: (f.id = af.flight_id)
        Buffers: shared hit=367361 read=248982
        ->  Index Scan using flights_id_idx on flights f  (cost=0.43..743949.15 rows=3106090 width=4) (actual time=0.066..24170.693 rows=3106983 loops=1)
              Buffers: shared hit=316162 read=85698
        ->  Index Only Scan using index_aggregated_flights_on_flight_id_and_flight_relation_id on aggregated_flights af  (cost=0.56..886207.11 rows=15357503 width=4) (actual time=0.014..31282.777 rows=15360252 loops=1)
              Heap Fetches: 0
              Buffers: shared hit=51199 read=163284
Planning time: 246.341 ms
Execution time: 58744.695 ms

更新我在aggregated_flights表上添加了一个索引,就在flight_id上。这肯定使查询更快,但我仍然认为10秒有点多。

app=> explain (analyze, buffers) select f.id from flights f left join aggregated_flights af on af.flight_id = f.id where af.flight_id is null and f.status = 'COMMITED' limit 1000;
                                                                                          QUERY PLAN                                                                                           
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
 Limit  (cost=3.83..453.80 rows=1000 width=4) (actual time=9986.052..9986.508 rows=470 loops=1)
   Buffers: shared hit=365265 read=126777
   ->  Merge Anti Join  (cost=3.83..1341784.78 rows=2981880 width=4) (actual time=9986.050..9986.437 rows=470 loops=1)
         Merge Cond: (f.id = af.flight_id)
         Buffers: shared hit=365265 read=126777
         ->  Index Scan using flights_id_idx on flights f  (cost=0.43..743949.15 rows=3106090 width=4) (actual time=0.935..3891.800 rows=3107353 loops=1)
               Buffers: shared hit=317084 read=84797
         ->  Index Only Scan using aggregated_flights_flight_id_idx on aggregated_flights af  (cost=0.43..398876.22 rows=15360252 width=4) (actual time=0.023..3270.955 rows=15360252 loops=1)
               Heap Fetches: 0
               Buffers: shared hit=48181 read=41980
 Planning time: 53.676 ms
 Execution time: 9986.603 ms
(12 rows)

1 个答案:

答案 0 :(得分:1)

使用连接列上的两个索引,其中一个部分可以容纳WHERE条件,您可以尽可能地提高查询速度。

PostgreSQL方面的唯一改进是部分索引上的仅索引扫描。为此,您应该VACUUM flights并使用PostgreSQL 9.6或更高版本,其中支持仅部分索引的索引扫描。

除此之外,最好的优化是为机器提供足够的RAM来缓存整个数据库(或至少包含所涉及的索引),这样就不必从磁盘读取数据。您可以使用pg_prewarm将表或索引加载到缓存中。