MySQLのindex初心者から脱したい

MySQLのindex初心者から脱したい

※私の解釈なので間違っていれば優しく教えてください

※個人的メモ程度

indexとは

一言で表すとSELECT文に合わせてレコードを並び替えたもの

Btreeの形になっているため二分探索ができる。

f:id:cloudBear:20190221213346g:plain
Btree

計算量は

O(N) >>> O(log2 N)

雲泥の差

複合index

2つ以上のカラムを基準に並び替える

ALTER TABLE comment ADD INDEX index1(thread_id, status, post_time);

この場合以下の順番でレコードが並ぶindexが作られる

thread_idで並び替える->複数あれば同じグループの中でstatusを並び替える-> さらにpostTimeで並び替える

MySQLの評価順位

indexで重要になるのがMySQLの評価順序 以下のような順番で評価する。

1. FROM
2. ON
3. JOIN
4. WHERE
5. GROUP BY
6. HAVING
7. SELECT
8. DISTINCT
9. ORDER BY
10. TOP(LIMIT)

有効なSELECT文

indexの並び順とMySQL評価順が一致!😄

SELECT 
    *
FROM
   thread 
LEFT JOIN 
   comment
ON
    thread.id = comment.thread_id
WHERE
    comment.status = 0
ORDER BY
    comment.post_time;

有効でないSELECT文

indexの並び順とMySQL評価順が一致しない!😓

SELECT 
    *
FROM
   thread 
LEFT JOIN 
   comment
ON
    thread.id = comment.thread_id
WHERE
    comment.post_time > 20181231
GROUP BY
    comment.status;

ちなみにindexで絞った結果が全体レコードの30%以上になるとオプティマイザがindexを使用するのを放棄する。

Explainよみかた

+----+-------------+---------------+--------+---------------+-----------+---------+---------------+-------+-------------+
| id | select_type | table         | type   | possible_keys | key       | key_len | ref           | rows  | Extra       |
+----+-------------+---------------+--------+---------------+-----------+---------+---------------+-------+-------------+
|  1 | SIMPLE      | hoge          | ALL    | NULL          | NULL      | NULL    | NULL          | 16913 | Using where |
|  1 | SIMPLE      | huge          | ref    | i1            | i1        | 4       | banana.t.id   |     1 | NULL        |
|  1 | SIMPLE      | poge          | eq_ref | coupon_id     | coupon_id | 4       | melon.t.id    |     1 | Using where |
|  1 | SIMPLE      | page          | ref    | i1            | i1        | 4       | tomato.t.id   |     1 | NULL        |
|  1 | SIMPLE      | hige          | ref    | i1            | i1        | 4       | cherry.t.id   |     1 | NULL        |
|  1 | SIMPLE      | pige          | ref    | i1            | i1        | 4       | apple.t.id    |     1 | NULL        |
+----+-------------+---------------+--------+---------------+-----------+---------+---------------+-------+-------------+

id

テーブルがどのグループに属するかをみることができる、基本id=1 サブクエリがある場合id=2,3出てくる

select_type

クエリの種類を表すもの、joinやjoinすらしないものはsimpleなので、基本simpleしか出てこない

以下はsimple以外のパターン。

  • PRIMARY・・・外部クエリを示す。
  • SUBQUERY・・・相関関係のないサブクエリ。
  • DEPENDENT SUBQUERY・・・相関関係のあるサブクエリ。
  • UNCACHEABLE SUBQUERY・・・実行する度に結果が変わる可能性のあるサブクエリ。
  • DERIVED・・・FROM句で用いられているサブクエリ。

    table

    アクセスされるテーブル

    type

    たぶん一番大事なフィールド、クリティカルなヤバい奴を探したいときはここを見る

  • const・・・PRIMARY KEYまたはUNIQUEインデックスのルックアップによるアクセス。最速。

  • eq_ref・・・JOINにおいてPRIARY KEYまたはUNIQUE KEYが利用される時のアクセスタイプ。constと似ているがJOINで用いられるところが違う。
  • ref・・・ユニーク(PRIMARY or UNIQUE)でないインデックスを使って等価検索(WHERE key = value)を行った時に使われるアクセスタイプ。
  • range・・・インデックスを用いた範囲検索。
  • index・・・フルインデックススキャン。インデックス全体をスキャンする必要があるのでとても遅い。
  • ALL・・・フルテーブルスキャン。インデックスがまったく利用されていないことを示す。やばい

    possible_keys

    オプティマイザがテーブルのアクセスに利用可能なインデックスの候補として挙げたキーの一覧。

    key

    オプティマイザによって選択されたキー。

    key_len

    選択されたキーの長さ。インデックスの走査は、キー長が短い方が高速。

    ref

    検索条件で、keyと比較されている値やカラムの種類。定数が指定されている場合はconstと表示される。JOINが実行されている時には、結合する相手側のテーブルで検索条件として利用されているカラムが表示される。例えば次の例では、CountryテーブルはCityテーブルとCity.CountryCodeカラムでJOINされるということを示している。

    rows

    そのテーブルからフェッチされる行数の見積もり。あくまでもテーブル全体の行数やインデックスの分散具合から導き出された大まかな見積もりなので、実際にフェッチされる正確な行数ではないので注意。また、フェッチされた全ての行がそのまま結果として返されるわけではないという点にも注意。後述するUsing whereがExtraフィールドに表示されている場合は、フェッチした行に対してさらにWHERE句の検索条件が適用されて行の絞り込みが行われるので、クライアントへ返される結果行は少なくなる可能性がある。

    Extra

    オプティマイザさんの独り言が表示される、でもとても大事。

  • Using where・・・WHERE句に検索条件が指定されており、なおかつインデックスを見ただけではWHERE句の条件を全て適用することが出来ない場合に表示される。
  • Using index・・・クエリがインデックスだけを用いて解決できることを示す。Covering Indexを利用している場合などに表示される。
  • Using filesort・・・filesort(クイックソート)でソートを行っていることを示す。
  • Using temporary・・・クエリの実行にテンポラリテーブルが必要なことを示す。
  • Using index for group-by・・・MIN()/MAX()がGROUP BY句と併用されているとき、クエリがインデックスだけを用いて解決できることを示す。