TT Lab
はじめる
学ぶ 学習パス コース

データベースの概念

インデックスを作り計画が変わるか確かめる

TT Labで続きを見る

目標

さまざまな種類のインデックスを自分で作り、実行計画がどう変わるかを目で確認します。インデックスの作り方よりも、インデックスがいつ使われ、いつ無視されるのかを知ることが、このラボの目的です。

なぜ重要なのか

インデックスを作ったのに計画が変わらないときに、最もよくあるアドバイスは、強制的に使わせるというものです。これは診断ではなく症状の抑え込みで、強制的に使わせると、かえって遅くなるケースが実際に多くあります。インデックススキャンは、一致した行ごとにヒープページをランダムに訪問するので、返す行が多いと、表全体をランダムな順序で読む形になるからです。

そのため、インデックスが使われないときに投げるべき質問は、2つに分かれます。オプティマイザーが正しいので使われないのか、自分のクエリがインデックスを使えない形なので使われないのか。前者は、インデックスを変えるか諦める必要があり、後者は、クエリを直す必要があります。対応が正反対なので、区別が先です。

このラボでは、部分インデックスと式インデックス、カバリングインデックスまで作りながら、「インデックスは1つではなく、何種類もある」という感覚を身につけます。

ステップ

  1. EXPLAIN SELECT count(*) FROM orders WHERE channel = 'partner';の出力を、/root/expではなく/root/plan_seq.txtに保存します。シーケンシャルスキャンが出る必要があります。
  2. orders (ordered_at)に、idx_orders_ordered_atという名前のB-treeインデックスを作ります。
  3. ordered_atに範囲条件をかけた照会のEXPLAIN出力を、/root/plan_idx.txtに保存します。計画にidx_orders_ordered_atが出る必要があります。
  4. order_itemsに、idx_order_items_order_productという名前で、(order_id, product_id)の順序の複合インデックスを作ります。
  5. orders (ordered_at)に、status = 'pending'の条件を付けた部分インデックスidx_orders_pendingを作ります。
  6. customersに、lower(email)に対する式インデックスidx_customers_email_lowerを作ります。
  7. productsに、キーのカラムは(category, price)、INCLUDEのカラムはnameのカバリングインデックスidx_products_cat_priceを作ります。
  8. シーケンシャルスキャンをオフにした状態で、ordered_atの条件でcount(*)を求めるEXPLAIN出力を、/root/plan_only.txtに保存します。Index Only Scanが出る必要があります。

参考

インデックスがないときの計画を残す

EXPLAIN SELECT count(*) FROM orders WHERE channel = 'partner';の出力を、/root/expではなく/root/plan_seq.txtに保存します。シーケンシャルスキャンが出る必要があります。

EXPLAINは、クエリの前に付けるだけです。まだインデックスがないカラムを条件に使うと、シーケンシャルスキャンが出ます。

基本のB-treeインデックスを作る

orders (ordered_at)に、idx_orders_ordered_atという名前のB-treeインデックスを作ります。

CREATE INDEX index_name ON table_name (column_name)の形です。インデックス名を正確に合わせないと、採点されません。

インデックスを使う計画を残す

ordered_atに範囲条件をかけた照会のEXPLAIN出力を、/root/plan_idx.txtに保存します。計画にidx_orders_ordered_atが出る必要があります。

範囲の狭い条件をかけると、オプティマイザーがインデックスを選びます。カラムに関数をかぶせると、インデックスを使えません。

複合インデックスのカラム順序を決める

order_itemsに、idx_order_items_order_productという名前で、(order_id, product_id)の順序の複合インデックスを作ります。

等号条件でよく使われるカラムが前に来る必要があります。順序が変わると、別のインデックスです。

部分インデックスでサイズを減らす

orders (ordered_at)に、status = 'pending'の条件を付けた部分インデックスidx_orders_pendingを作ります。

CREATE INDEX ... WHERE conditionの形で、一部の行だけを含められます。サイズを比べてみてください。

式インデックスを作る

customersに、lower(email)に対する式インデックスidx_customers_email_lowerを作ります。

カラムではなく、計算結果でインデックスを作れます。括弧で囲む必要があり、クエリも同じ式を使わないと使われません。

カバリングインデックスでヒープの訪問を減らす

productsに、キーのカラムは(category, price)、INCLUDEのカラムはnameのカバリングインデックスidx_products_cat_priceを作ります。

探索に使うキーのカラムと、結果にだけ必要なカラムを区別して定義できる句があります。

Index Only Scanを観察する

シーケンシャルスキャンをオフにした状態で、ordered_atの条件でcount(*)を求めるEXPLAIN出力を、/root/plan_only.txtに保存します。Index Only Scanが出る必要があります。

表が小さいと、オプティマイザーはシーケンシャルスキャンを選びます。診断の目的で、シーケンシャルスキャンを一時的に止めて、計画を取り直してみてください。