web-dev-qa-db-ja.com

MySQLクエリと複数列範囲クエリのインデックスを最適化する

私はこのようなテーブルを持っています:

CREATE TABLE IF NOT EXISTS `jobs` (
    `job_id` varchar(36) NOT NULL,
    `job_status` varchar(30) NOT NULL,
    `created_at` datetime NOT NULL,
    `lease_date` datetime,
    `priority` int NOT NULL,
    PRIMARY KEY(`job_id`),
    INDEX `job_status_priority_lease_date` (`job_status`, `priority`, `lease_date`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin;

テーブルに対して実行する完全なクエリは次のようになります。

select * from jobs 
where job_status="IN_PROGRESS"
and lease_date<"10minutesago"
order by 
priority
limit 100

データベースをジョブキューとして実装しています。 10分はジョブタイムアウトのようなものです。既にタイムアウトしたジョブのみを処理したいと思います。仕事の優先順位が違うので注文しました。また、DBジョブキューをFIFOのように動作させたいので、インデックスにlease_dateを含めています。

結果を優先順位で並べ替えたいと思います。優先度0および1の結果の2つのチャンク。結果がlease_dateによって厳密に並べられている必要はありませんが、それらが私にとって十分な独自のチャンクで昇順である必要があります。例えば結果セット:

priority lease_date
0        2017-08-19
0        2018-09-20
1        2016-08-20
1        2018-10-20

私のインデックスはそれで十分ですか?クエリを最適化してインデックスを作成し、パフォーマンスを可能な限り向上させるにはどうすればよいですか?

1
Jialun Liu

_"10minutesago"_とはNOW() - INTERVAL 10 MINUTEですか?

_order by priority DESC_並べ替えのみpriority。その中の順序は予測できません。昇順、降順、またはランダムに見える場合があります。

あなたのINDEX(job_status, priority, lease_date)は_lease_date_まで到達しません。 _where job_status="IN_PROGRESS"_とmightが処理され、priorityの順序で行が処理されます。しかし、それだけです。

あなたcouldは_ORDER BY priority DESC, lease_date DESC_と言いますが、_and lease_date < "10minutesago"_はインデックスを使用しません。

したがって、最初に_=_でテストされた任意の数の列を作成します。次にone range列を指定します。

(OPの編集後)

あなたの例はあなたが必要とすることを意味します

_ORDER BY priority ASC, lease_date ASC
_

効率の問題については...

WHERE句には

_INDEX(job_status, lease_date)  -- in this order; adding `priority` won't help
_

オプティマイザが_ORDER BY_に集中することを好む場合は、

_INDEX(job_status, priority, lease_date)  -- in this order
_

ノート:

  • _job_status_でテストされているため、_=_が最初です。
  • _lease_date_だけをタックすると、インデックス内のWHERE全体を処理できるようになります。しかし、_ORDER BY_を処理するための「ファイルソート」はまだ存在します。
  • オプティマイザを2番目のインデックスで(ファイルソートを回避するために)_ORDER BY_に集中するように誘惑すると、逆効果になる場合とそうでない場合があります。 _lease_date_フィルターのためにスキップする必要があるデータの量によって異なります。

テーブルに1000行ある場合は、両方のインデックスを追加するだけです。クエリは「十分に高速」になります。 100万行ある場合、難しい問題に直面しているため、キューイングメカニズムを再考する必要があります。インデックスをどう処理しても、間違ったインデックスが使用され、クエリの実行が非常に遅くなることがあります。システムに一時的な障害が発生すると、キューが文明的な数のエントリから膨大な数に散発的にジャンプする可能性があることに注意してください。

このテーブルが「キュー」であることを意味しますか?キュー内のアイテムを実行するのにどのくらい時間がかかりますか?それが「短い」時間である場合、「それをキューに入れないでください、それをしてください」。

priorityをWHEREに追加します

に変更した場合

_select * from jobs 
    where job_status = "IN_PROGRESS"
      AND priority = 1
    and lease_date < NOW() - 10 MINUTE
    order by priority
    limit 100
_

次に、これらのいずれかが最適になります。

_INDEX(job_status, priority, lease_date)
INDEX(priority, job_status, lease_date)
_

議論については my Cookbook を参照してください。

priorityありとなしの混合がある場合は、次の2つを用意します。

_INDEX(job_status, lease_date),
INDEX(job_status, priority, lease_date)
_

オプティマイザーはそれらの間で選択します。

0
Rick James