如何理解EXPLAIN ANALYZE

Nie*_*ian 38 postgresql performance database-design postgresql-performance

我对查看EXPLAIN ANALYZE结果不是很熟悉,我的查询太慢了,我遇到了很大的问题.我试图阅读如何解释解释查询的结果,但我仍然不知道我应该寻找什么,以及可能出错的地方.我有一种感觉,某处有一些大红灯闪烁,我只是看不到它.

所以查询很简单,看起来像这样:

EXPLAIN ANALYZE SELECT "cars".* FROM "cars" WHERE "cars"."sales_state" = 'onsale' AND "cars"."brand" = 'BMW' AND "cars"."model_name" = '318i' AND "cars"."has_auto_gear" = TRUE  LIMIT 25 OFFSET 0
Run Code Online (Sandbox Code Playgroud)

结果如下:

Limit  (cost=0.00..161.07 rows=25 width=1245) (actual time=35.232..38.694 rows=25 loops=1)
  ->  Index Scan using index_cars_onsale_on_brand_and_model_name on cars  (cost=0.00..1179.06 rows=183 width=1245) (actual time=35.228..38.652 rows=25 loops=1)
        Index Cond: (((brand)::text = 'BMW'::text) AND ((model_name)::text = '318i'::text))
        Filter: has_auto_gear"
Total runtime: 38.845 ms
Run Code Online (Sandbox Code Playgroud)

一点背景:我在Postgresql 9.1.6上,在Herokus专用数据库上运行.我的数据库有大约7,5Gb内存,表车包含3,1M行,并且行中有2,0M的行有sales_state ='onsale'.该表有170列.它使用的索引如下所示:

CREATE INDEX index_cars_onsale_on_brand_and_model_name
  ON cars
  USING btree
  (brand COLLATE pg_catalog."default" , model_name COLLATE pg_catalog."default" )
  WHERE sales_state::text = 'onsale'::text;
Run Code Online (Sandbox Code Playgroud)

有人看到一些很明显的问题吗?

编辑:

SELECT pg_relation_size('cars'), pg_total_relation_size('cars');
Run Code Online (Sandbox Code Playgroud)

pg_relation_size:2058444800 pg_total_relation_size:4900126720

SELECT pg_relation_size('index_cars_onsale_on_brand_and_model_name');
Run Code Online (Sandbox Code Playgroud)

pg_relation_size:46301184

SELECT avg(pg_column_size(cars)) FROM cars limit 5000;
Run Code Online (Sandbox Code Playgroud)

平均值:636.9732567210792995

没有限制:

EXPLAIN ANALYZE SELECT "cars".* FROM "cars" WHERE "cars"."sales_state" = 'onsale' AND "cars"."brand" = 'BMW' AND "cars"."model_name" = '318i' AND "cars"."has_auto_gear" = TRUE

Bitmap Heap Scan on cars  (cost=12.54..1156.95 rows=183 width=4) (actual time=17.067..55.198 rows=2096 loops=1)
  Recheck Cond: (((brand)::text = 'BMW'::text) AND ((model_name)::text = '318i'::text) AND ((sales_state)::text = 'onsale'::text))
  Filter: has_auto_gear
  ->  Bitmap Index Scan on index_cars_onsale_on_brand_and_model_name  (cost=0.00..12.54 rows=585 width=0) (actual time=15.211..15.211 rows=7411 loops=1)"
        Index Cond: (((brand)::text = 'BMW'::text) AND ((model_name)::text = '318i'::text))
Total runtime: 56.851 ms
Run Code Online (Sandbox Code Playgroud)

Cra*_*ger 28

虽然对于像这样的简单计划不那么有用,但http://explain.depesz.com非常有用.请参见http://explain.depesz.com/s/t4fi.请注意"统计信息"标签和"选项"下拉菜单.

有关此计划的注意事项:

  • 估计的行数(183)与实际行数(25)相当.它不是数百倍,也不是1.当涉及行数估计或"1 vs 1"问题时,您对数量级更感兴趣.(你甚至不需要"足够接近政府工作"的准确性 - "足够接近军事承包会计"会这样做).选择性估计和统计似乎是合理的.

  • 它使用了提供的两列部分索引(index scan using index_cars_onsale_on_brand_and_model_name),因此它与部分索引条件匹配.你可以在中看到Filter: has_auto_gear.还显示了索引搜索条件.

  • 鉴于表的行数意味着索引相当大,特别是因为它超过两列,查询性能看起来很合理.匹配的行将被分散,因此每行可能还需要单独的页面读取.

我觉得这里没有错.不过,这个查询可能会从PostgreSQL 9.2的仅索引扫描中受益匪浅.

这里可能存在一些表膨胀,但考虑到2列索引和行数,响应时间并非完全不合理,特别是对于具有170(!!)列的表,每个列可能适合相对较少的元组页.如果您能够承受一些停机时间,请尝试VACUUM FULL重新组织表并重建索引.这将在重建它时将表独占锁定一段时间.如果你不能承受的停机时间,见pg_reorg和/或CREATE INDEX CONCURRENTLYALTER INDEX ... RENAME TO.

EXPLAIN (ANALYZE, BUFFERS, VERBOSE)有时您可能会发现更多信息,因为它可以显示缓冲区访问等.

可以使此查询更快的一个选项(尽管它可能会在某种程度上减慢其他查询的速度)是对表进行分区brand并启用constraint_exclusion.请参阅分区.