これがクエリです:
SELECT "products".*
FROM "products"
WHERE (status > 100)
AND "products"."above_revenue_average" = 't'
AND ("products"."category_id" NOT IN (5))
ORDER BY "products"."start_date" DESC
status
とstart_date
にインデックスがあります。
アプリケーションからクエリを実行するたびに、ログに次の情報が記録されます。
[WHITE] temporary file:
path "pg_tblspc/16386/PG_9.3_201306121/pgsql_tmp/pgsql_tmp2544.0", size 37093376
Query: SELECT "products".* FROM "products" WHERE (status > 100)
AND "products"."above_revenue_average" = 't'
AND ("products"."category_id" NOT IN (5)) ORDER BY "products"."start_date" DESC
この一時ファイルの作成がパフォーマンスの低下の原因だと思います。
EXPLAIN ANALYZE
を実行すると、次の結果が得られます。
QUERY PLAN
Sort (cost=63395.28..63403.51 rows=16460 width=524)
(actual time=524.134..562.635 rows=65294 loops=1)
Sort Key: start_date
Sort Method: external merge Disk: 36224kB
-> Bitmap Heap Scan on products
(cost=4803.40..60389.73 rows=16460 width=524)
(actual time=27.390..397.879 rows=65294 loops=1)
Recheck Cond: (status > 100)
Filter: (above_revenue_average AND (category_id <> 5))
Rows Removed by Filter: 25115
-> Bitmap Index Scan on index_products_on_status
(cost=0.00..4802.58 rows=89662 width=0)
(actual time=18.006..18.006 rows=90411 loops=1)
Index Cond: (status > 100)
Total runtime: 577.870 ms
(10 rows)
次に http://explain.depesz.com/ を使用して、少し読みやすくしました。
+-------------------+-------+--------------+------------+
| node type | count | sum of times | % of query |
+-------------------+-------+--------------+------------+
| Bitmap Heap Scan | 1 | 379.873 ms | 67.5 % |
| Bitmap Index Scan | 1 | 18.006 ms | 3.2 % |
| Sort | 1 | 164.756 ms | 29.3 % |
+-------------------+-------+--------------+------------+
+------------------+------------+--------------+------------+
| Table name | Scan count | Total time | % of query |
+------------------+------------+--------------+------------+
| scan type | count | sum of times | % of table |
| products | 1 | 379.873 ms | 67.5 % |
| Bitmap Heap Scan | 1 | 379.873 ms | 100.0 % |
+------------------+------------+--------------+------------+
さらにインデックスを追加して、データベースのパフォーマンスを向上させることはできますか?多分いくつかのものですか?何か案は?
work_mem
明らかに、ソート操作はディスクに流出します。
Sort Method: external merge Disk: 36224kB
より多くのwork_mem
は、 @ Kassandryがすでに提案しているように クエリを支援できます。 Memory
出力にDisk
ではなくEXPLAIN
が表示されるまで設定を増やします。ただし、1つのクエリに基づいて一般的な設定を増やすことはおそらく悪い考えです。適切な設定は、利用可能なRAMおよび完全な状況によって異なります。 Postgres Wiki を読むことから始めます。
クエリを修正するには、同じトランザクションでwork_mem
のみを使用して、トランザクションに十分なSET LOCAL
を設定します。
BEGIN;
SET LOCAL work_mem = '45MB';
SELECT ...
COMMIT; -- or ROLLBACK
おそらく40 MBを少し超える容量が必要です。インメモリ表現は、ディスク上の表現よりも少し大きいです。関連:
クエリ(ノイズを削除した後):
SELECT *
FROM products
WHERE status > 100
AND above_revenue_average -- boolean can be used directly
AND category_id <> 5
ORDER BY start_date DESC;
行の幅は0.5キロバイトです(width=524
)。すべての列を返す必要がありますか? (通常、必要ありません。)work_mem
の問題が既に発生しているため、全体的なパフォーマンスを向上させるために、クエリから必要なSELECT
リストの列のみをリストします。
関連する列のいずれかをNULLにすることはできますか? category_id
およびstart_date
では特に重要です。あなたはその場合に適応したいかもしれません...
複数列のインデックスは確かにパフォーマンスに役立ちます。 ( @ Paulと同じように )コストと利益を比較検討する必要があります。このクエリのパフォーマンスが重要であるか、非常に一般的である場合は、それを実行してください。クエリごとに特別なインデックスを作成しないでください。できるだけ少なく、必要なだけ。インデックスを共有するとより強力になるため、より多くのインデックスがキャッシュに残る可能性が高くなります。
above_revenue_average
のようなboolean
列は、インデックス列ではなく、部分インデックスの条件の典型的な候補です。
不完全な情報に基づく私の大まかな推測:
CREATE INDEX prod_special_idx ON products (start_date DESC)
WHERE above_revenue_average
AND status > 100
AND category_id <> 5;
インデックスでDESC NULLS LAST
を使用し、start_date
をNULLにできるかどうかを照会します。
この種類のクエリのパフォーマンスを向上させる最も簡単で最も効果的な方法の1つは、SET work_mem=40MB
を実行して(並べ替え用の一時ファイルが32MB以下あり、追加の作業が頻繁に役立つため)、クエリを実行して確認することですEXPLAIN ANALYZE
プランがディスクからメモリ内ソートに変更された場合。その後、RESET work_mem
を実行して、値をpostgresql.conf
のデフォルト値に戻します。
基本的なアイデアは、次のリンクのwork_memで説明されています: PostgreSQLサーバーのチューニング 。個々のクエリのこの設定を変更すると、利点が得られ、言及された欠点が回避されます。
お役に立てば幸いです。 =)
結合された述語の選択性に応じて、この特定のクエリに適したインデックスは次のようになると思います。
CREATE INDEX index_name
ON products
(above_revenue_average ASC, start_date DESC)
WHERE
status > 100
AND category_id <> 5;
SELECT *
は、上記のインデックスにすべての列が含まれていないため、潜在的に問題があります。これにより、プランナーでインデックスが選択されない場合は、最初に主キー列のみをフェッチしてから、テーブルに結合して、選択したキーのみの追加の列を収集できます。