如何理解EXPLAIN ANALYZE

时间:2012-10-16 12:55:10

标签: performance postgresql 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

结果如下:

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

一点背景: 我在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;

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

编辑:

SELECT pg_relation_size('cars'), pg_total_relation_size('cars');

pg_relation_size:2058444800 pg_total_relation_size:4900126720

SELECT pg_relation_size('index_cars_onsale_on_brand_and_model_name');

pg_relation_size:46301184

SELECT avg(pg_column_size(cars)) FROM cars limit 5000;

平均值: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

2 个答案:

答案 0 :(得分: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。请参阅partitioning

答案 1 :(得分:0)

嗯......我能告诉你的第一件事是你的数据库期望(从统计数据中)获得183行。实际上它有25行。虽然在这种情况下可能不太相关(即,这些数量很少且没有繁重的操作,所以不必担心错误地估算它)。

更大的问题(imho)是25行的简单索引查找需要35ms。这看起来有点多了。数据库是否足够重,至少在内存中包含所有索引?但这并不过分,对我来说似乎有点慢。

至于查看解释,我建议使用explain.depesz.com:http://explain.depesz.com/s/sA6