6

パラメータを使用する場合とクエリ文字列にその値をハードコーディングする場合、同じクエリの実行に長い時間がかかるこのPostgresの問題が発生しています。列名は「media_type」で、VARCHAR(20)です。私はPHPからSymfony2とDoctrine2ORMを使用してこれらのクエリを実行しており、問題のテーブルには約1.000.000レコードがあります。

クエリに問題がありますか?Postgresの設定の問題でしょうか?

1-media_typeのハードコードされた値

duration: 5.365 ms  parse pdo_stmt_00000001: SELECT id,site_id FROM item where media_type = 'Collection' AND enabled = 'true' AND site_id = $1 AND user_id = $2 ORDER BY id DESC LIMIT $3 OFFSET $4
duration: 0.142 ms  bind pdo_stmt_00000001: SELECT id,site_id FROM item where media_type = 'Collection' AND enabled = 'true' AND site_id = $1 AND user_id = $2 ORDER BY id DESC LIMIT $3 OFFSET $4
parameters: $1 = '1', $2 = '1', $3 = '100', $4 = '0'
duration: 8.667 ms  execute pdo_stmt_00000001: SELECT id,site_id FROM item where media_type = 'Collection' AND enabled = 'true' AND site_id = $1 AND user_id = $2 ORDER BY id DESC LIMIT $3 OFFSET $4
parameters: $1 = '1', $2 = '1', $3 = '100', $4 = '0'

実行計画:

duration: 8.640 ms  plan:
    Query Text: SELECT id,site_id FROM item where media_type = 'Collection' AND enabled = 'true' AND site_id = $1 AND user_id = $2 ORDER BY id DESC LIMIT $3 OFFSET $4
    Limit  (cost=8.38..8.38 rows=1 width=12) (actual time=8.516..8.595 rows=24 loops=1)
      Buffers: shared hit=10 read=15
        ->  Sort  (cost=8.38..8.38 rows=1 width=12) (actual time=8.505..8.530 rows=24 loops=1)
            Sort Key: id
            Sort Method: quicksort  Memory: 26kB
            Buffers: shared hit=10 read=15
            ->  Index Scan using item_media_type_index on item  (cost=0.00..8.37 rows=1 width=12) (actual time=7.955..8.397 rows=24 loops=1)
                    Index Cond: ((media_type)::text = 'Collection'::text)
                    Filter: (enabled AND (site_id = $1) AND (user_id = $2))
                    Buffers: shared hit=8 read=15

2-media_type(SLOWER)のパラメーターを使用する

duration: 5.557 ms  parse pdo_stmt_00000001: SELECT id,site_id FROM item where media_type = $1 AND enabled = 'true' AND site_id = $2 AND user_id = $3 ORDER BY id DESC LIMIT $4 OFFSET $5
duration: 1.322 ms  bind pdo_stmt_00000001: SELECT id,site_id FROM item where media_type = $1 AND enabled = 'true' AND site_id = $2 AND user_id = $3 ORDER BY id DESC LIMIT $4 OFFSET $5
parameters: $1 = 'Collection', $2 = '1', $3 = '1', $4 = '100', $5 = '0'
duration: 71564.998 ms  execute pdo_stmt_00000001: SELECT id,site_id FROM item where media_type = $1 AND enabled = 'true' AND site_id = $2 AND user_id = $3 ORDER BY id DESC LIMIT $4 OFFSET $5
parameters: $1 = 'Collection', $2 = '1', $3 = '1', $4 = '100', $5 = '0'

実行計画:

duration: 71564.922 ms  plan:
    Query Text: SELECT id,site_id FROM item where media_type = $1 AND enabled = 'true' AND site_id = $2 AND user_id = $3 ORDER BY id DESC LIMIT $4 OFFSET $5
    Limit  (cost=90663.16..181326.31 rows=17184 width=12) (actual time=3.667..71564.864 rows=24 loops=1)
      Buffers: shared hit=183786 read=96585
        ->  Index Scan Backward using item_pkey on item  (cost=0.00..906610.46 rows=171836 width=12) (actual time=3.655..71564.798 rows=24 loops=1)
               Filter: (enabled AND ((media_type)::text = $1) AND (site_id = $2) AND (user_id = $3))
               Buffers: shared hit=183786 read=96585

前もって感謝します。

4

3 に答える 3

9

これはPostgreSQLの長年の疣贅であり、これを回避するには、歴史的に興味深いプランナーの調整が必要でした。いつものようにTomLaneに感謝しますが、PostgreSQL 9.2(現在はベータ版)で修正されています。

E.1.3.1.3。オプティマイザ

パラメータ化されたプランを選択するプランナーの能力を向上させる(Tom Lane)

プリペアドステートメントは現在、解析、分析、および書き換えられていますが、必ずしも計画されているわけではありません。準備された計画がパラメーターを使用して実行される場合、プランナーは定数ごとに計画を再計画するか、コストが定数固有の計画のコストに近い場合は一般的な計画を実行する可能性があります。

9.2ベータリリースノートと、これについてlwn.netに書いた簡単なノートを参照してください。メーリングリストの通常のステートメントよりも実行が遅い準備済み/パラメータ化されたステートメントの処理に関する多くの情報があります。

于 2012-05-31T06:46:26.727 に答える
2

SMALLINTフィールドにバインドし、Postgresが暗黙的にINTEGERからSMALLINTにキャストしている値を渡したときに、非常によく似た問題が発生しました。キャストを明示的にすることで修正しました。media_typeはタイプVARCHAR(20)であるため、PostgresはタイプTEXTからの暗黙的なキャストを実行しています。これを試して:

where media_type = $1::VARCHAR(20)

于 2012-05-30T23:56:16.353 に答える
1

静的で高速なクエリでは、item_media_type_indexが使用されています。バインドされた低速のクエリでは、item_media_type_indexは使用されていません。

「item」テーブルの「media_type」列の選択性はどのくらいですか?あなたがした場合:

SELECT media_type, COUNT(*)
  FROM item
  GROUP BY media_type
  ORDER BY 2 desc

media_typesは均等にバランスが取れていますか、それとも他のメディアタイプと比較して「コレクション」media_typesが比較的少ないですか?'Collection'アイテムが比較的少ない場合は、この推測を危険にさらします。静的クエリを使用すると、パーサーは'Collection'をクエリしていることを認識し、'Collection'のカウントが少なく、インデックスを使用する価値があると判断できます。ただし、バインド変数の場合、パーサーは使用しているmedia_typeを認識しません。他のmedia_type値は、テーブル内のレコードの高い割合(たとえば、20%)を占める可能性があります。その場合、インデックスを使用するよりもスキャンを実行する方が高速です。パーサーは決定を下す必要があり、たまたまインデックスを使用しないことを決定します(あなたの場合は間違っていますが、おそらく他のmedia_typeでは正しいです)。

この場合、選択性属性が非常に偏っていることがわかっている場合はハードコーディングするか、動的SQLを使用して遅延解析を強制するか、正しいと思われる場合はインデックスの使用を強制することが答えです。

于 2012-05-31T00:29:41.870 に答える