5

クエリを最適化するためにコストと実際の時間をどのように使用する必要があるかを理解するために取り組んでいます。私のアプリは、PostgreSQL 9.1 db を使用した rails 3 です。私のクエリは Delayed_job で使用されます:

EXPLAIN ANALYZE SELECT  "delayed_jobs".*
FROM "delayed_jobs"
WHERE ((run_at <= '2011-05-23 15:16:43.180810' AND (locked_at IS NULL OR locked_at < '2011-01-25 11:05:28.077144') OR locked_by = 'host:foo pid:2') AND failed_at IS NULL AND queue = 'authentication_emails')
ORDER BY priority ASC, run_at ASC LIMIT 5

または:

EXPLAIN ANALYZE SELECT  "delayed_jobs".*
FROM "delayed_jobs"
WHERE ((run_at <= '2011-05-23 15:16:43.180810' AND (locked_at IS NULL OR locked_at < '2011-01-25 11:05:28.077144') OR locked_by = 'host:foo pid:2') AND failed_at IS NULL )
ORDER BY priority ASC, run_at ASC LIMIT 5

最初のクエリの場合、出力は次のようになります。

Limit  (cost=7097.57..7097.57 rows=1 width=1008) (actual time=35.657..35.657 rows=0 loops=1)
  ->  Sort  (cost=7097.57..7097.57 rows=1 width=1008) (actual time=35.655..35.655 rows=0 loops=1)
        Sort Key: priority, run_at
        Sort Method: quicksort  Memory: 25kB
        ->  Seq Scan on delayed_jobs  (cost=0.00..7097.56 rows=1 width=1008) (actual time=35.648..35.648 rows=0 loops=1)
              Filter: ((failed_at IS NULL) AND ((queue)::text = 'authentication_emails'::text) AND (((run_at <= '2011-05-23 15:16:43.18081'::timestamp without time zone) AND ((locked_at IS NULL) OR (locked_at < '2011-01-25 11:05:28.077144'::timestamp without time zone))) OR (locked_by = 'host:foo pid:2'::text)))
Total runtime: 35.695 ms

現在、テーブルには 90k のレコードがあり、範囲は 0 ~ 200k です。このクエリが原因で CPU が急増し、ボトルネックが発生していることに気付きました。上記の説明情報から何がわかるか。インデックスがある場合、どこに追加する必要がありますか? ありがとう

DB スキーマ..テーブルには 0 のインデックスがあります。

  create_table "delayed_jobs", :force => true do |t|
    t.integer  "priority",   :default => 0
    t.integer  "attempts",   :default => 0
    t.text     "handler"
    t.text     "last_error"
    t.datetime "run_at"
    t.datetime "locked_at"
    t.datetime "failed_at"
    t.text     "locked_by"
    t.datetime "created_at",                :null => false
    t.datetime "updated_at",                :null => false
    t.string   "queue"
  end
4

2 に答える 2

1

分析

PostgreSQL ドキュメント のこのセクションに進むと、プランナーが統計を使用してコストを見積もる方法を学習できます。とても使える情報です!

そのテーブルに約 90,000 件のレコードがあり (デフォルトの cost を使用)、行の処理のコストは次のようになります。

90000 * (cpu_tuple_cost + cpu_operator_cost) = 90000 * 0.0125 = 1125

テーブルが占めるページ数を概算できます。

(7097.56-1125)/seq_page_cost = 5972.56

これにより、およそ 46Mb になります (デフォルトの 8k ページ サイズ)。したがって、デフォルトのものであっても、あなたのテーブルはshared_buffersに収まると思います。

平均行幅を見ると、そのテーブルはほとんどが として保存されてMAINいると思います。

text次に、タイプのフィールドをstring述語として使用しています。それらがPostgreSQLの内部型にどのようにマッピングされるかはわかりませんが、text. この型はデフォルトで圧縮可能であるため、PostgreSQL は述語をチェックするために行ごとに圧縮解除を実行する必要があります。どのしきい値の圧縮が開始されるかはわかりません。このメッセージ(およびスレッド全体) を見てください。

結論

  1. EXPLAIN (analyze)また、35ミリ秒のクエリがボトルネックを引き起こす可能性があるとは思わないため、実際の出力は示していません...
  2. ボトルネックの瞬間にデータベースを使用しているセッションの数について言及していません。また、このクエリが実行される頻度も明確ではありません。非常に人気のあるものだと思います。
  3. テーブルはメモリに収まるように見えるため、すべての操作いずれにしても CPU バウンドになります。
  4. 述語で使用される値は圧縮可能であり、圧縮されているように見えます。

したがって、ボトルネックは、データに対して並行して実行されるクエリのピーク量に起因すると言いました。これは、圧縮解除のために余分な CPU サイクルを必要とします。

何をすべきか?

  1. テーブルを正規化します。「キュー」列の選択性が非常に低いと感じます。外部型( など) の作成を検討するENUMか、適切な外部キーを使用してディクショナリ テーブルを編成します。locked_by列もわかりませんが、正規化できますか?
  2. run_atおよび列に索引を作成しlocked_atます。
  3. インデックスON priority, run_at列は並べ替えに役立ちますが、この場合には役立つとは思えません。列の選択性が低いと想定しているpriorityため、プランナーはonと列よりも使用Bitmap Andすることを好みます。Index Scansrun_atlocked_at

ここで私がひどく間違っていないことを願っています:) コメント/修正は大歓迎です!

PS どうなるか教えてください。

于 2013-01-29T10:18:25.953 に答える
0

Where should indexes be added?

言い換えると、特定の SQL クエリには、インデックスの優れた候補となる列がありません。大量の履歴データがある場合、日時列で < 演算子を使用すると、(おそらく) 大きな結果セットが返されます。条件を満たしたときにインデックスから返される結果セットが大きいほど、特定のクエリのコンテキストでのインデックスの効率が低下します (削減する能力が低下します)。さらに、一部のデータベースでは NULL がインデックス化されないため、NULL のテストにはテーブル スキャンが必要です。インデックスの NULL 値に関して、PostgreSQL 9.1 については不明です。

于 2013-02-05T18:39:21.017 に答える