我有一些航班(约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分钟。
对于约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)
使用连接列上的两个索引,其中一个部分用于容纳WHERE
条件,您可以尽可能地提高查询速度。
PostgreSQL方面唯一的改进是仅对部分索引进行索引扫描。为此,您应该使用VACUUM flights
并使用PostgreSQL 9.6或更高版本,其中支持仅部分索引的索引扫描。
除此之外,最好的优化是为机器提供足够的RAM来缓存整个数据库(或至少涉及的索引),这样就不必从磁盘读取数据。您可以使用pg_prewarm
将表或索引加载到缓存中。