44

我对查看 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 的 RAM,表 cars 包含 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
4

2 回答 2

33

虽然对于像这样的简单计划没有那么有用,但http://explain.depesz.com确实很有用。见http://explain.depesz.com/s/t4fi。注意“统计”选项卡和“选项”下拉菜单。

关于这个计划的注意事项:

  • 估计的行数 (183) 与实际行数 (25) 相当。它不是数百倍,也不是 1。当涉及到行数估计或“1 与非 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 CONCURRENTLYand ALTER INDEX ... RENAME TO

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

一种可能使该查询更快的选项(尽管它可能会降低其他查询的速度)是对表进行分区brand并启用constraint_exclusion. 请参阅分区

于 2012-10-16T13:06:47.253 回答
0

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

一个更大的问题(恕我直言)是 25 行的简单索引查找需要 35 毫秒。这似乎有点多。数据库是否足够重,至少可以在内存中包含所有索引?不过这并不过分,对我来说似乎有点慢。

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

于 2012-10-16T13:10:43.510 回答