11. • インデックスはツリー構造
• データはソートされている
• リーフノードに値と行のPKを格納
なぜ、インデックスで高速化するのか?
PK col1 time
1 a 11:15
2 f 01:10
3 d 03:01
: : :
SELECT * FROM t WHERE col1 = ‘f’
インデックス対象
a-p r-z
a
pk=1
d
pk=3
f
pk=2
p
pk=9
r
pk=4
t
pk=5
12. カーディナリティ
mysql> show index from t;
+-----+----------+----------+------------+-<SNIP>-+-----------+-------------+
|Table|Non_unique| Key_name |Seq_in_index| | Collation | Cardinality |
+-----+----------+----------+------------+--------+-----------+-------------+
| t | 0| PRIMARY | 1| | A | 5 |
+-----+----------+----------+------------+--------+-----------+-------------+
1 row in set (0.00 sec)
インデックス内のユニークな値
の多さを表した指数
Copyright (C) 2016 Yahoo Japan Corporation. All Rights Reserved. 無断引用・転載禁止
15. PK is_pk_1000multi
(PKが1000の倍数?)
time
1 0 11:15
: 0 12:31
1000 1 13:44
: : 14:01
: : :
1000000 1 20:01
例外
0
pk=1
pk=2
pk=3
:
:
:
1
pk=1000
pk=2000
pk=3000
:
SELECT * FROM t WHERE is_pk_1000multi = 1
AND time <= 12:00
• 分布が偏っていれば効果が大きくなる
• 条件によって効果が異なる
インデックス対象
×
1000回
Copyright (C) 2016 Yahoo Japan Corporation. All Rights Reserved. 無断引用・転載禁止
16. Left-Most-Index
以下のインデックスは同じ?
違います
インデックスA.
CREATE INDEX idxA1 ON mytable (col1);
CREATE INDEX idxA2 ON mytable (col2);
インデックスB.
CREATE INDEX idxB1 ON mytable (col1, col2);
インデックスC.
CREATE INDEX idxC1 ON mytable (col2, col1);
Copyright (C) 2016 Yahoo Japan Corporation. All Rights Reserved. 無断引用・転載禁止
17. 複合インデックスでは指定順が重要
a
pk=1
9 a
pk=2
8 b
pk=3
7 b
pk=4
6
5
pk=1
c 6
pk=4
b 7
pk=2
b 8
pk=3
a
インデックスB.
CREATE INDEX idxB1 ON mytable (col1, col2);
インデックスC.
CREATE INDEX idxC1 ON mytable (col2, col1);
a b~c
5~6 7~9
col2col1
col1col2
c
pk=5
5
9
pk=5
a
18. 複合インデックスでは指定順が重要
絞り込み対象カラム
→
1.WHRER col1 =
‘x’
2.WHERE col2 =
‘x’
3.WHERE col1 = ‘x’ AND col2 = ‘x’
インデックスA idxA1
が利用される
idxA2
が利用される
idxA1, idxA2 どちらかが利用される。
※
インデックスB idxB1
が利用される
インデックスは利
用されない
idxB1 が利用される。
idxA1, idxA2より高速。
インデックスC インデックスは利
用されない
idxC1
が利用される
idxC1が利用される。
idxA1, idxA2より高速。
※ 場合によってはインデックスマージ
インデックスA.
CREATE INDEX idxA1 ON mytable (col1);
CREATE INDEX idxA2 ON mytable (col2);
インデックスB.
CREATE INDEX idxB1 ON mytable (col1, col2);
インデックスC.
CREATE INDEX idxC1 ON mytable (col2, col1);
27. 実行計画の確認
• 実行計画=クエリの処理の流れ
• 「EXPLAIN」をクエリの先頭に付ける
• 更新系クエリはSELECTに書き換え
UPDATE t SET col = newvalue WHERE condition = ‘x’;
EXPLAIN SELECT col FROM t WHERE condition = ‘x’;
Copyright (C) 2016 Yahoo Japan Corporation. All Rights Reserved. 無断引用・転載禁止
28. EXPLAINの結果の例
mysql> EXPLAIN SELECT t1.pk, t1.col1, t2.col2
FROM t1 INNER JOIN t2
ON (t1.pk = t2.fk)
WHERE t1.pk = 1;
+----+------------+------+------+--------------------+--------+--------+------+-----+-------------+
| id | select_type| table| type | possible_keys | key | key_len| ref | rows| Extra |
+----+------------+------+------+--------------------+--------+--------+------+-----+-------------+
| 1 | SIMPLE | t1 | const| PRIMARY,pk_col1_idx| PRIMARY| 4 | const| 1| |
| 1 | SIMPLE | t2 | ALL | NULL | NULL | NULL | NULL | 5| Using where |
+----+------------+------+------+--------------------+--------+--------+------+-----+-------------+
カラム 説明
id SELECTごとに振られるID。処理順ではない点に注意。
select_type SELECTの種類。SIMPLE, SUBQUERY, UNIONなど。
type テーブルへのアクセス方法。インデックスの利用有無、読取範囲などがわかる。
possible_keys 利用可能なインデックス。
key 実際に利用されたインデックス。possible_keys からインデックスの内容や統計情報を加味して、選択さ
れたインデックス。
key_len 読み取ったインデックスのバイト数。
ref 比較するカラム。constの場合は定数(WHERE x = 1のような場合)。
rows スキャンする見積もり行数。JOINやサブクエリが関係する場合は外部表のrows × 内部表のrowsがス
キャンする行になる。
Extra その他の情報
34. インデックスが効かない条件
Extra
関数 WHERE datediff(now(), mod_date) > '180'
式 WHERE col1 / 2 = 0
否定構文 WHERE col1 != 3
LIKE検索
※ 前方一致除く
WHERE col1 LIKE ‘%string%’
WHERE col1 LIKE ‘%string’
Copyright (C) 2016 Yahoo Japan Corporation. All Rights Reserved. 無断引用・転載禁止
35. 【1.フルスキャン】チューニング結果
mysql> CREATE INDEX idx01 ON push_info(deleted, mod_date);
mysql> EXPLAIN SELECT push_id FROM push_info
WHERE deleted=1 AND mod_date < date_sub(NOW(), INTERVAL 180 DAY);
+----+-------------+-----------+-------+-------+------+------+--------------------------+
| id | select_type | table | type | key | ref | rows | Extra |
+----+-------------+-----------+-------+-------+------+------+--------------------------+
| 1 | SIMPLE | push_info | range | idx01 | NULL | 10 | Using index |
+----+-------------+-----------+-------+-------+------+------+--------------------------+
インデックスの
範囲読み込み
行数が10行 インデックスだ
けで解決でき
るクエリ
• Before: 380msec → After: 0msec
Copyright (C) 2016 Yahoo Japan Corporation. All Rights Reserved. 無断引用・転載禁止
36. 代表的なチューニング例
1. type=ALL または type=index で rows が大きい
2. Extra に Using temporary; Using filesort でrowsが大きい
3. select_type が DEPENDENT SUBQUERY
4. JOINにおいて2つ目以降のExtraにUsing whereが出力さていて
rows が大きい
5. 大量更新
6. データ削除
Copyright (C) 2016 Yahoo Japan Corporation. All Rights Reserved. 無断引用・転載禁止
37. 【2.ソート】
mysql> EXPLAIN SELECT order_time,seller_id,image_id,item_id,<省略>
FROM order_master WHERE is_hidden_page = '0'
ORDER BY order_time DESC LIMIT 20;
+--+-----------+--------------+----+---------------+----+----------+-----------------------------+
|id|select_type| table |type| possible_keys |key | rows | Extra |
+--+-----------+--------------+----+---------------+----+----------+-----------------------------+
| 1|SIMPLE | order_master |ALL | NULL |NULL| 12200494 | Using where; Using filesort |
+--+-----------+--------------+----+---------------+----+----------+-----------------------------+
• is_hidden_page は殆どの行で0
• カーディナリティが低いため×
• ソートにインデックスは有効か?
• LIMIT句がある場合に効果が高い
1200万件
ソートしている
Copyright (C) 2016 Yahoo Japan Corporation. All Rights Reserved. 無断引用・転載禁止
38. 【2.ソート】チューニング結果
mysql> CREATE INDEX OM_OTM ON order_master (order_time);
mysql> EXPLAIN SELECT order_time,seller_id,image_id,item_id,<省略>
FROM order_master WHERE is_hidden_page = '0'
ORDER BY order_time DESC LIMIT 20;
+--+-------------+--------------+-----+---------------+--------+------+-------------+
|id| select_type | table |type | possible_keys | key | rows | Extra |
+--+-------------+--------------+-----+---------------+--------+------+-------------+
| 1| SIMPLE | order_master |index| NULL | OM_OTM | 20 | Using where |
+--+-------------+--------------+-----+---------------+--------+------+-------------+
20個だけ取る
• インデックスはソート済みのため、ソートが不要
• 上位20件だけ取ったら、処理を終了
• Before: 4300sec → After: 0sec
ソートがなくなるインデックス利用
Copyright (C) 2016 Yahoo Japan Corporation. All Rights Reserved. 無断引用・転載禁止
41. 代表的なチューニング例
1. type=ALL または type=index で rows が大きい
2. Extra に Using temporary; Using filesort でrowsが大きい
3. select_type が DEPENDENT SUBQUERY
4. JOINにおいて2つ目以降のExtraにUsing whereが出力さていて
rows が大きい
5. 大量更新
6. データ削除
Copyright (C) 2016 Yahoo Japan Corporation. All Rights Reserved. 無断引用・転載禁止
42. 【3.相関サブクエリ】
mysql> EXPLAIN SELECT team_name
FROM team
WHERE team_id IN (SELECT team_id FROM member WHERE skill IN ("C"));
+----+--------------------+--------+------+---------------+省略+------+-------------+
| id | select_type | table | type | possible_keys | | rows | Extra |
+----+--------------------+--------+------+---------------+----+------+-------------+
| 1 | PRIMARY | team | ALL | NULL | | 3 | Using where |
| 2 | DEPENDENT SUBQUERY | member | ALL | NULL | | 5 | Using where |
+----+--------------------+--------+------+---------------+----+------+-------------+
team_id team_name
1 Team1
2 Team2
3 Team3
team_id member_name skill
1 Yahoo Taro C
1 Yahoo Jiro Python
2 Yahoo Hanako Ruby
2 Yahoo Saburo PHP
3 Yahoo Sirou Perl
■ member■ team
Copyright (C) 2016 Yahoo Japan Corporation. All Rights Reserved. 無断引用・転載禁止
43. 【3.相関サブクエリ】 期待する動作
team_id team_name
1 Team1
2 Team2
3 Team3
team_id member_name skill
1 Yahoo Taro C
1 Yahoo Jiro Python
2 Yahoo Hanako Ruby
2 Yahoo Saburo PHP
3 Yahoo Sirou Perl
mysql> EXPLAIN SELECT team_name
FROM team
WHERE team_id IN (SELECT team_id FROM member WHERE skill IN ("C"));
最初に実行される
ことを期待
①skill=Cを探す②team_id=1を探す
• memberテーブルを1回読み込む
• teamテーブルを1回読み込む
• 合計=5 + 3 = 8行の読み込みを期待する
44. 【3.相関サブクエリ】 実際の動き
mysql> EXPLAIN SELECT team_name
FROM team
WHERE team_id IN (SELECT team_id FROM member WHERE skill IN ("C"));
team_id team_name
1 Team1
2 Team2
3 Team3
team_id member_name skill
1 Yahoo Taro C
1 Yahoo Jiro Python
2 Yahoo Hanako Ruby
2 Yahoo Saburo PHP
3 Yahoo Sirou Perl
team_id =1 AND skill=C
のレコードを探す
• 合計= 3 + 3 * 5 = 18行の読み込み
• 実行計画の「PRIMARY」は外部表(親)を示す
• なぜ、期待どうりに動かないのか?
• MySQLの仕様です。。。(5.6で改善されます)
45. 【3.相関サブクエリ】 チューニング方法(1)
mysql> CREATE INDEX idx_skill ON member(skill, team_id);
mysql> EXPLAIN SELECT team_name
FROM team
WHERE team_id IN (SELECT team_id FROM member WHERE skill IN ("C"));
+----+--------------------+--------+------+---------------+-----------+省略+------+-------------+
| id | select_type | table | type | possible_keys | key | | rows | Extra |
+----+--------------------+--------+------+---------------+-----------+省略+------+-------------+
| 1 | PRIMARY | team | ALL | NULL | NULL | | 3 | Using where |
| 2 | DEPENDENT SUBQUERY | member | ref | idx_skill | idx_skill | | 1 | Using where |
+----+--------------------+--------+------+---------------+-----------+省略+------+-------------+
•サブクエリの実行にインデックスを利用
•3 + 3 * 1 = 6行に(見積もり)
+----+--------------------+--------+------+---------------+省略+------+-------------+
| id | select_type | table | type | possible_keys | | rows | Extra |
+----+--------------------+--------+------+---------------+----+------+-------------+
| 1 | PRIMARY | team | ALL | NULL | | 3 | Using where |
| 2 | DEPENDENT SUBQUERY | member | ALL | NULL | | 5 | Using where |
+----+--------------------+--------+------+---------------+----+------+-------------+
インデックス未使用
46. 【3.相関サブクエリ】 チューニング方法(2)
mysql> EXPLAIN SELECT team_name
FROM team JOIN member USING(team_id)
WHERE member.skill IN ("C");
+----+-------------+--------+--------+---------------+-----------+---------------------+------+
| id | select_type | table | type | possible_keys | key | ref | rows |
+----+-------------+--------+--------+---------------+-----------+---------------------+------+
| 1 | SIMPLE | member | ref | idx_skill | idx_skill | const | 1 |
| 1 | SIMPLE | team | eq_ref | PRIMARY | PRIMARY | test.member.team_id | 1 |
+----+-------------+--------+--------+---------------+-----------+---------------------+------+
member表を先に読む
• JOINに書き換える
• 相関サブクエリ、だいたいJOINで書き直せる
• オプティマイザが親子を自動判断
• member表を外部表(駆動表)にする
+----+--------------------+--------+------+---------------+-----------+省略+------+-------------+
| id | select_type | table | type | possible_keys | key | | rows | Extra |
+----+--------------------+--------+------+---------------+-----------+省略+------+-------------+
| 1 | PRIMARY | team | ALL | NULL | NULL | | 3 | Using where |
| 2 | DEPENDENT SUBQUERY | member | ref | idx_skill | idx_skill | | 1 | Using where |
+----+--------------------+--------+------+---------------+-----------+省略+------+-------------+
47. 代表的なチューニング例
1. type=ALL または type=index で rows が大きい
2. Extra に Using temporary; Using filesort でrowsが大きい
3. select_type が DEPENDENT SUBQUERY
4. JOINにおいて2つ目以降のExtraにUsing whereが出力さ
ていて rows が大きい
5. 大量更新
6. データ削除
Copyright (C) 2016 Yahoo Japan Corporation. All Rights Reserved. 無断引用・転載禁止
60. 必要なデータが大きいもの
SELECT COUNT(*) FROM access_log;
SELECT SUM(a) FROM access_log WHERE data BETWEEN ‘1995-01-01’ AND NOW();
必要なデータが大きいものは遅い
• MySQLは1セッション=1スレッド=1CPU
• 作りから見直す必要がある
• 例) 集計テーブルを作り更新時に同時にカウ
ントアップする
Copyright (C) 2016 Yahoo Japan Corporation. All Rights Reserved. 無断引用・転載禁止