61
MySQL社内講習 インデックス編 CROOZ Team Venus

MySQL勉強会 インデックス編.2013 08-02

Embed Size (px)

Citation preview

Page 1: MySQL勉強会 インデックス編.2013 08-02

MySQL社内講習 インデックス編

CROOZ Team Venus

Page 2: MySQL勉強会 インデックス編.2013 08-02

目次

•  インデックスとは •  EXPLAINとは •  インデックスが使えない場合 •  インデックスの種類 •  インデックスの制限事項 •  複合インデックス •  カバリングインデックス •  まとめ •  参考資料

Page 3: MySQL勉強会 インデックス編.2013 08-02

『インデックスとは』

Page 4: MySQL勉強会 インデックス編.2013 08-02

インデックスとは 目次のことです

Page 5: MySQL勉強会 インデックス編.2013 08-02

目次が在るから目的のページが探せる

Page 6: MySQL勉強会 インデックス編.2013 08-02

目次がないと….。

Page 7: MySQL勉強会 インデックス編.2013 08-02

最初から全部 読まなければならない

Page 8: MySQL勉強会 インデックス編.2013 08-02

目次は万能じゃない

•  ページがしょっちゅう増えるとその都度目次も作り直す

•  目次のページ数自体が大きすぎると本末転倒

•  適正に目次を作成して使用するのが大事

Page 9: MySQL勉強会 インデックス編.2013 08-02

事例に基づいて 考えて見ましょう

Page 10: MySQL勉強会 インデックス編.2013 08-02

アイテムの付与ミスったー

Page 11: MySQL勉強会 インデックス編.2013 08-02

取り消しバッチを作れー!

Page 12: MySQL勉強会 インデックス編.2013 08-02

付与したtimestampを 条件に論理削除すれば

良いよね

Page 13: MySQL勉強会 インデックス編.2013 08-02

付与したtimestampを 条件に論理削除すれば

良いよね 死亡フラグ

Page 14: MySQL勉強会 インデックス編.2013 08-02

demo

Page 15: MySQL勉強会 インデックス編.2013 08-02

対象テーブル

・贈り物テーブル ・ヒストリーデータ ・サンプルのデータは180万件

Page 16: MySQL勉強会 インデックス編.2013 08-02

mysql> UPDATE prize_history SET del_flg = 1 WHERE prize_id = 14 AND ctime BETWEEN '2013-05-18 00:00:00' AND '2013-05-24 00:00:00';

実行するSQL

イベント報酬ID

イベント開始日時

イベント終了日時

Page 17: MySQL勉強会 インデックス編.2013 08-02

どうすれば良かったのか?

Page 18: MySQL勉強会 インデックス編.2013 08-02

まずはEXPLAINを実行

Page 19: MySQL勉強会 インデックス編.2013 08-02

EXPLAINとは

Page 20: MySQL勉強会 インデックス編.2013 08-02

EXPLAINとは

•  SQLの実行計画を見るクエリー

•  インデックスの使用状況を確認できる

•  実行するSQLの先頭にEXPLAINをつける (※ DELETE文/UPDATE文の場合はSELECT文に置き換える) (※ WHERE句があるSQLはインデックスが使われる)

Page 21: MySQL勉強会 インデックス編.2013 08-02

mysql> EXPLAIN SELECT * FROM prize_history WHERE prize_id = 14 AND ctime BETWEEN '2013-05-18 00:00:00' AND '2013-05-24 00:00:00'\G;*************************** 1. row *************************** id: 1 select_type: SIMPLE table: prize_history type: ALLpossible_keys: NULL key: NULL key_len: NULL ref: NULL rows: 2233733 Extra: Using where

【クエリーのタイプ】:全件検索

【使用されているインデックス】: インデックスが使われていない

【検索行数】:テーブル全行数

【追加情報(処理方法)】:Using temoraryと、 Using filesortは要注意、それぞれ、一時書き出し、ファイルソートが発生していてクエリーが遅くなる傾向がある

Page 22: MySQL勉強会 インデックス編.2013 08-02

SHOW INDEX FROM prize_history;+----------------------+------------+----------+--------------+-----------------------+-----------+-------------+----------+--------+------+------------+---------+---------------+

| Table | Non_unique | Key_name | Seq_in_index | Column_name | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment | Index_comment |+----------------------+------------+----------+--------------+-----------------------+-----------+-------------+----------+--------+------+------------+---------+---------------+

| prize_history | 0 | PRIMARY | 1 | prize_history_id | A | 2235476 | NULL | NULL | | BTREE | | || prize_history | 1 | user_id | 1 | user_id | A | 2235476 | NULL | NULL | | BTREE | | || prize_history | 1 | user_id | 2 | receive_flg | A | 2235476 | NULL | NULL | | BTREE | | |

| prize_history | 1 | user_id | 3 | prize_id | A | 2235476 | NULL | NULL | | BTREE | | |+----------------------+------------+----------+--------------+-----------------------+-----------+-------------+----------+--------+------+------------+---------+---------------+

4 rows in set (5.73 sec)

インデックスを確認する

ctimeのインデックスがない

インデックス名 インデックス種別 対象カラム

プライマリキー プライマリキー prize_history user_id インデックス user_id,receive_flg,prize_id

Page 23: MySQL勉強会 インデックス編.2013 08-02

ALTER TABLE prize_history ADD KEY prize_id (prize_id,ctime);EXPLAIN SELECT * FROM prize_history WHERE prize_id = 14 AND ctime BETWEEN '2013-05-18 00:00:00' AND '2013-05-24 00:00:00'\G;*************************** 1. row *************************** id: 1 select_type: SIMPLE table: prize_history type: rangepossible_keys: prize_id key: prize_id key_len: 12 ref: NULL rows: 186246 Extra: Using where;

インデックス追加

Page 24: MySQL勉強会 インデックス編.2013 08-02

■ BEFOREmysql> SELECT SQL_NO_CACHE count(*) FROM prize_history WHERE prize_id = 14 AND ctime BETWEEN '2013-05-18 00:00:00' AND '2013-05-24 00:00:00';+----------+| count(*) |+----------+| 86399 |+----------+1 row in set (3.62 sec)

■ AFTER

mysql> SELECT SQL_NO_CACHE count(*) FROM prize_history WHERE prize_id = 14 AND ctime BETWEEN '2013-05-18 00:00:00' AND '2013-05-24 00:00:00';+----------+| count(*) |+----------+| 86399 |+----------+1 row in set (0.06 sec)

BEFORE/AFTER

60x FAST

Page 25: MySQL勉強会 インデックス編.2013 08-02

インデックスが使えない場合

•  テーブルの後ろから読みLIMITで制限をかける。ヒストリー系のテーブルで有効

•  使えるインデックスに変換する

Page 26: MySQL勉強会 インデックス編.2013 08-02

ctimeを プライマリキーに変換

イベント期間

2013/04/23

2013/05/18 2013/05/24 2013/05/30

× 最初からフルスキャンするにはデータが多すぎる

2181000

1

2008199 2094599

ctimeをプライマリキーに

変換 データの終わりから フルスキャン指定期間に達したらクエリ終了

Page 27: MySQL勉強会 インデックス編.2013 08-02

対象となる期間の プライマリキーのIDを検索

Page 28: MySQL勉強会 インデックス編.2013 08-02

SELECT prize_history_id FROM prize_history WHERE ctime < '2013-05-18 00:00:00' ORDER BY prize_history_id DESC LIMIT 1;+-----------------------+| prize_history_id |+-----------------------+| 2008199 |+-----------------------+

イベント開催日時 ※バックアップサーバーで実行

Page 29: MySQL勉強会 インデックス編.2013 08-02

SELECT prize_history_id FROM prize_history WHERE ctime < '2013-05-24 00:00:00' ORDER BY prize_history_id DESC LIMIT 1;+-----------------------+| prize_history_id |+-----------------------+| 2094599 |+-----------------------+

イベント終了日時 ※バックアップサーバーで実行

Page 30: MySQL勉強会 インデックス編.2013 08-02

mysql> EXPLAIN SELECT * FROM prize_history WHERE prize_id = 14 AND prize_history_id BETWEEN 2008199 AND 2094599\G;*************************** 1. row *************************** id: 1 select_type: SIMPLE table: prize_history type: rangepossible_keys: PRIMARY key: PRIMARY key_len: 8 ref: NULL rows: 174524 Extra: Using where

期間をプライマリキーに 置き換えてEXPLAIN

Page 31: MySQL勉強会 インデックス編.2013 08-02

UPDATE prize_history SET del_flg=1 WHERE prize_id = 14 AND prize_history_id BETWEEN 2008199 AND 2094599;

UPDATE文を作成

×

Page 32: MySQL勉強会 インデックス編.2013 08-02

1つのSQLでまとめて update/insert/delete しない

Page 33: MySQL勉強会 インデックス編.2013 08-02

詳しくは 次回『リプリケーション』編で

Page 34: MySQL勉強会 インデックス編.2013 08-02

1行更新する UPDATE文を作成する

•  プログラムでselectした結果からforで回して、一行更新するのupdate文を実行するバッチを作る

•  SELECTした結果をEXCELに貼ってマクロでUPDATE文を作成する

•  SQLで1行更新するupdate文のSQLを作成

Page 35: MySQL勉強会 インデックス編.2013 08-02

SQLで1行更新する update文のSQLを作成

Page 36: MySQL勉強会 インデックス編.2013 08-02

SELECT CONCAT( 'UPDATE prize_history ', 'SET del_flg = 1 ', 'WHERE prize_history_id =', prize_history_id,';' ) FROM prize_history WHERE prize_id = 14 AND prize_history_id BETWEEN 2008199 AND 2094599;

UPDATE文作成 ※バックアップサーバーで実行

Page 37: MySQL勉強会 インデックス編.2013 08-02

バックアップ作成

SELECT CONCAT( 'UPDATE prize_history ', 'SET del_flg = ',del_flg,' ', 'WHERE prize_history_id =', prize_history_id,';' ) FROM prize_history WHERE prize_id = 14 AND prize_history_id BETWEEN 2008199 AND 2094599;

※バックアップサーバーで実行

Page 38: MySQL勉強会 インデックス編.2013 08-02

『インデックスの種類』

Page 39: MySQL勉強会 インデックス編.2013 08-02

インデックスの種類

• プライマリキー • ユニークキー • インデックス

Page 40: MySQL勉強会 インデックス編.2013 08-02

インデックスの制限事項

•  !=、<>はインデックスが使用できない

• LIKE検索では前方一致のみ使用できる。

Page 41: MySQL勉強会 インデックス編.2013 08-02

『複合インデックスとは』

Page 42: MySQL勉強会 インデックス編.2013 08-02

複数のカラムに対する インデックス

複合インデックスとは

Page 43: MySQL勉強会 インデックス編.2013 08-02

複合インデックスは 先頭から順に部分インデックス

として使用できる。

Page 44: MySQL勉強会 インデックス編.2013 08-02

つまり

•  index(user_id,category_id,del_flg) という複合インデックスがあった場合 ×index(user_id,category_id) ×index(user_id) のインデックスは作る必要がない

Page 45: MySQL勉強会 インデックス編.2013 08-02

テスト用テーブル

・サンプルのデータは10万件

カラム名 型

id unsinged int(11)

A unsinged int(11)

B varchar(255) C unsinged int(11)

インデックス名

インデックス種別

対象カラム

pkey プライマリキー Id

index_A インデックス A

index_B インデックス B

index_C インデックス C

index_A_B_C

インデックス A,B,C

Page 46: MySQL勉強会 インデックス編.2013 08-02

複合インデックス が使える場合

•  SELECT * FROM test WHERE A=1 and B=2 and C=3

•  SELECT * FROM test WHERE A=1 and B=2

•  SELECT * FROM test WHERE A=1 ※ INDEX index_a_b_c(A,B,C)の場合

Page 47: MySQL勉強会 インデックス編.2013 08-02

複合インデックス が使えない場合

•  SELECT * FROM test WHERE B=2 and C=3

•  SELECT * FROM test WHERE A=1 and C=3

•  SELECT * FROM test WHERE B=2

※ INDEX index_a_b_c(A,B,C)の場合

Page 48: MySQL勉強会 インデックス編.2013 08-02

複合インデックスは 順番が重要

Page 49: MySQL勉強会 インデックス編.2013 08-02

カバリング インデックスとは

Page 50: MySQL勉強会 インデックス編.2013 08-02

対象となるすべての 検索条件・検索項目を含んだ

複合インデックス

Page 51: MySQL勉強会 インデックス編.2013 08-02

カバリングインデックスの例

•  SELECT A,B,C FROM test WHERE A=1 and B=2 and C=3

•  SELECT A FROM test WHERE A=1 and B=2

•  SELECT A,B,C FROM test WHERE A=1

※ INDEX index_a_b_c(A,B,C)の場合

Page 52: MySQL勉強会 インデックス編.2013 08-02

カバリングインデックス EXPLAIN

EXPLAIN SELECT A,B,C FROM test WHERE A=757 AND b='0.ZYc2FHB0kpo' \G; *************************** 1. row *************************** id: 1 select_type: SIMPLE table: test type: ref possible_keys: index_A,index_B,index_A_B_C key: index_A_B_C key_len: 771 ref: const,const rows: 1 Extra: Using where; Using index

Indexを使っているという意味ではない。Index内のデータを使用しているという意味(カバリングインデックス)

Page 53: MySQL勉強会 インデックス編.2013 08-02

インデックススキャンだけで 完結しているので

非常に高速

Page 54: MySQL勉強会 インデックス編.2013 08-02

通常の場合

インデックス

データベース

クエリ

結果セット

インデックスを元にデータベース

を参照

Page 55: MySQL勉強会 インデックス編.2013 08-02

カバリングインデックスの場合

インデックス

クエリ

結果セット

InnoDBの場合、インデックスにデータが含まれる為、対象データがインデックスに存在すれば、データベース参照なしで結果セットを返す。

Page 56: MySQL勉強会 インデックス編.2013 08-02

だだし、サマリーテーブルを 作るようなものなので、 ディスク容量に注意

Page 57: MySQL勉強会 インデックス編.2013 08-02

まとめ

• ムダなインデックスは作らない。

• インデックスは出来るだけ1つの複合インデックスで複数カバー出来るように作る。カラムの順番が重要。

Page 58: MySQL勉強会 インデックス編.2013 08-02

次回予告

Page 59: MySQL勉強会 インデックス編.2013 08-02

インデックスが効かないとどうなるか

Page 60: MySQL勉強会 インデックス編.2013 08-02

MySQL社内講習 リプリケーション編

Page 61: MySQL勉強会 インデックス編.2013 08-02

『参考資料』 ・ソーシャルゲーム開発者なら知っておきたい MySQL INDEX + EXPLAIN入門 ・MySQL5からのインデックス結合で1テーブル複数インデックスを使う ・実践ハイパフォーマンスMySQL 第2版