webdevqa.jp.net

EXPLAIN ANALYZEを理解する方法

EXPLAIN ANALYZEの結果を見ることにあまり慣れていません。クエリが遅すぎるという大きな問題があります。 Explainクエリの結果を解釈する方法を読み上げようとしましたが、何を探していて、何が間違っているのかまだわかりません。どこかで大きな赤い光が点滅しているように感じますが、私はそれを見ません。

したがって、クエリは非常に単純で、次のようになります。

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

少しの背景:私は、Herokus専用データベースで実行しているPostgresql 9.1.6を使用しています。私のデータベースには約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
37
Niels Kristian

このような単純な計画にはそれほど有用ではありませんが、 http://explain.depesz.com は本当に便利です。 http://explain.depesz.com/s/t4fi を参照してください。 「統計」タブと「オプション」プルダウンに注意してください。

この計画に関する注意事項:

  • 推定行数(183)は、実際の行数(25)とほぼ同等です。行数の見積もりや「1対1以外」の問題に関しては、桁違いに興味があります。 (「政府の仕事に十分近い」正確さも必要ありません-「軍事契約会計に十分近い」必要があります)。選択性の見積もりと統計は合理的なようです。

  • 提供されている2列の部分インデックス(_index scan using index_cars_onsale_on_brand_and_model_name_)を使用しているため、部分インデックス条件に一致しています。それは_Filter: has_auto_gear_で見ることができます。インデックス検索条件も表示されます。

  • テーブルの行カウントがインデックスをかなり大きくすることを考えると、特に2列を超えるため、クエリのパフォーマンスは妥当に見えます。一致する行は散在するため、各行も個別のページ読み取りが必要になる可能性があります。

ここには何も問題はありません。ただし、このクエリは、PostgreSQL 9.2のインデックスのみのスキャンから大きな恩恵を受ける可能性があります。

ここにテーブルが肥大化する可能性がありますが、2列のインデックスと大量の行を考えると、特にそれぞれに比較的少ないタプルが収まる可能性がある170(!!)列のあるテーブルの場合、応答時間は完全に不合理ではありませんページ。ダウンタイムに余裕がある場合は、_VACUUM FULL_を試して、テーブルを再編成し、インデックスを再構築してください。これにより、テーブルを再構築している間、しばらくの間排他的にロックされます。ダウンタイムに余裕がない場合は、 pg_reorg および/または_CREATE INDEX CONCURRENTLY_および_ALTER INDEX ... RENAME TO_を参照してください。

バッファアクセスなどを表示できるため、EXPLAIN (ANALYZE, BUFFERS, VERBOSE)のほうが役立つ場合があります。

このクエリを高速化するオプションの1つは(他のクエリが多少遅くなるリスクはありますが)、brandでテーブルをパーティション化し、_constraint_exclusion_を有効にすることです。 パーティション分割 を参照してください。

28
Craig Ringer

さて...最初に言えることは、データベースが(統計から)183行を取得することを期待しているということです。実際には、25行を取得しています。これはおそらくこの場合にはあまり関係がありませんが(つまり、これらの少量で重い操作がない場合、間違って推定することを心配する必要はありません)。

より大きな問題(imho)は、25行の単純なインデックス検索に35msかかっていることです。それは少し多いようです。データベースは、少なくともすべてのインデックスをメモリ内に保持するのに十分ですか?それは過剰ではありませんが、私には少し遅いようです。

あなたの説明を見るには、explain.depesz.comを使用することをお勧めします。 http://explain.depesz.com/s/sA6

0
Wolph