MySQL はindex 貼ると早くなるってくらいの知識しかないのですが、どうすれば早くなるのか試してみました。
試した時の手順をメモとして残しておきます。
(index は貼るものか作るものか付けるものかよくわかりませんが、以下は貼るで統一しておきます。)
基本操作の確認
index の貼り方
ALTER TABLE <テーブル名> ADD INDEX <インデックス名>(<カラム名>);
または
CREATE INDEX <テーブル名> ON <インデックス名>(<カラム名>)
index の削除の仕方
ALTER TABLE <テーブル名> DROP INDEX <インデックス名>;
index の確認方法
SHOW INDEX FROM <テーブル名>;
また、EXPLAIN <調べたいクエリ> とコマンドを打つことで、クエリの実行方法を調査してくれます。
試してみる
確認用の適当なテーブルを作ります。
通し番号、カテゴリー、適当な数字の3つの行を持つテーブルです。
10000000行ほどデータを突っ込みます。
mysql> create table itest3 (id int, category varchar(5), num int);
Query OK, 0 rows affected (0.02 sec)
mysql> load data infile "<PATH_TO_FILE>" into table itest3 fields terminated by ","
Query OK, 10000000 rows affected (30.01 sec)
Records: 10000000  Deleted: 0  Skipped: 0  Warnings: 0
データは以下のスクリプトで作りました。
# !/usr/bin/python
# -*- coding: utf-8 -*-
import random
row_number = 10000000
category = ["t1", "t2", "t3", "t4", "t5"]
for x in xrange(row_number):
    line = str(x) + "," + category[int(random.random()*5)] + "," + str(int(random.random()*100))
    print line
%  python create.py > test.csv
適当なクエリを試してみます。
mysql>  select count(*) from itest3 where category = "t2";
+----------+
| count(*) |
+----------+
|  1999825 |
+----------+
1 row in set (4.00 sec)
mysql> select count(*) from itest3 where category = "t2" and num > 80 ;
+----------+
| count(*) |
+----------+
|   380036 |
+----------+
1 row in set (4.32 sec)
category で検索、category とnum で検索どちらもだいたい4秒くらいかかりました。
index を貼ってみます。
mysql> alter table itest3 add index category_index(category);
Query OK, 0 rows affected (19.58 sec)
Records: 0  Duplicates: 0  Warnings: 0
mysql> show index from itest3;
+--------+------------+----------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
| Table  | Non_unique | Key_name       | Seq_in_index | Column_name | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment | Index_comment |
+--------+------------+----------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
| itest3 |          1 | category_index |            1 | category    | A         |           4 |     NULL | NULL   | YES  | BTREE      |         |               |
+--------+------------+----------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
1 row in set (0.00 sec)
category にindex を貼ってみました。
SHOW INDEX コマンドで現在のidnex が確認できます。
| フィールド名 | 意味 | 
|---|---|
| Table | テーブル名 | 
| Non_unique | インデックスが重複を含まないなら 0 | 
| Key_name | インデックス名 | 
| Seq_in_index | インデックスのフィールド番号。1 から始まります。 | 
| Column_name | index を貼っているカラムの名前 | 
| Collation | インデックスでの ソート方法です。A (Ascending) か NULL (Not sorted) があります。 | 
| Cardinality | インデックスのユニークな値の数。 | 
| Sub_part | もしこのフィールドがインデックスに一部分だけ使用している場合、そのインデックスに使用しているキャラクター数をしめす。 もしキー全体がインデックスされているなら NULL 。 | 
| Comment | Various remarks. For now, it tells whether index is FULLTEXT or not. | 
mysql>  select count(*) from itest3 where category = "t2";
+----------+
| count(*) |
+----------+
|  1999825 |
+----------+
1 row in set (0.75 sec)
同じクエリが4秒->0.75 秒とかなり高速化されていることがわかります。
mysql> select count(*) from itest3 where category = "t2" and num > 80 ;
+----------+
| count(*) |
+----------+
|   380036 |
+----------+
1 row in set (1 min 20.11 sec)
でもこちらのクエリは4.32秒->1分20秒に。。。
こちらはとんでもなく遅くなっていました。
次にcategory とnum に複合index を貼ってみました。
mysql> alter table itest3 add index category_and_num_index(category,num);
Query OK, 0 rows affected (27.99 sec)
Records: 0  Duplicates: 0  Warnings: 0
mysql> show index from itest3;
+--------+------------+------------------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
| Table  | Non_unique | Key_name               | Seq_in_index | Column_name | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment | Index_comment |
+--------+------------+------------------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
| itest3 |          1 | category_index         |            1 | category    | A         |           4 |     NULL | NULL   | YES  | BTREE      |         |               |
| itest3 |          1 | category_and_num_index |            1 | category    | A         |           4 |     NULL | NULL   | YES  | BTREE      |         |               |
| itest3 |          1 | category_and_num_index |            2 | num         | A         |         501 |     NULL | NULL   | YES  | BTREE      |         |               |
+--------+------------+------------------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
3 rows in set (0.00 sec)
先ほど激遅だったクエリを実行してみると……
mysql> select count(*) from itest3 where category = "t2" and num > 80 ;
+----------+
| count(*) |
+----------+
|   380036 |
+----------+
1 row in set (0.21 sec)
超高速化されていました(約80秒->0.21秒)
最初のindex で早くなったクエリを実行してみると、
mysql> select count(*) from itest3 where category = "t2";
+----------+
| count(*) |
+----------+
|  1999825 |
+----------+
1 row in set (0.73 sec)
速いままでした。
以下にindex を貼る前と後で、同じクエリに対してEXPLAIN を実行した結果を載せておきます。
はじめ
mysql> explain select count(*) from itest3 where category = "t2";
+----+-------------+--------+------------+------+---------------+------+---------+------+----------+----------+-------------+
| id | select_type | table  | partitions | type | possible_keys | key  | key_len | ref  | rows     | filtered | Extra       |
+----+-------------+--------+------------+------+---------------+------+---------+------+----------+----------+-------------+
|  1 | SIMPLE      | itest3 | NULL       | ALL  | NULL          | NULL | NULL    | NULL | 10777272 |    10.00 | Using where |
+----+-------------+--------+------------+------+---------------+------+---------+------+----------+----------+-------------+
1 row in set, 1 warning (0.00 sec)
mysql> explain select count(*) from itest3 where category = "t2" and num > 80;
+----+-------------+--------+------------+------+---------------+------+---------+------+----------+----------+-------------+
| id | select_type | table  | partitions | type | possible_keys | key  | key_len | ref  | rows     | filtered | Extra       |
+----+-------------+--------+------------+------+---------------+------+---------+------+----------+----------+-------------+
|  1 | SIMPLE      | itest3 | NULL       | ALL  | NULL          | NULL | NULL    | NULL | 10777272 |     3.33 | Using where |
+----+-------------+--------+------------+------+---------------+------+---------+------+----------+----------+-------------+
1 row in set, 1 warning (0.00 sec)
alter table itest3 add index category_index(category); の後
mysql> explain select count(*) from itest3 where category = "t2";
+----+-------------+--------+------------+------+----------------+----------------+---------+-------+---------+----------+-------------+
| id | select_type | table  | partitions | type | possible_keys  | key            | key_len | ref   | rows    | filtered | Extra       |
+----+-------------+--------+------------+------+----------------+----------------+---------+-------+---------+----------+-------------+
|  1 | SIMPLE      | itest3 | NULL       | ref  | category_index | category_index | 18      | const | 3690834 |   100.00 | Using index |
+----+-------------+--------+------------+------+----------------+----------------+---------+-------+---------+----------+-------------+
1 row in set, 1 warning (0.00 sec)
mysql> explain select count(*) from itest3 where category = "t2" and num > 80;
+----+-------------+--------+------------+------+----------------+----------------+---------+-------+---------+----------+-------------+
| id | select_type | table  | partitions | type | possible_keys  | key            | key_len | ref   | rows    | filtered | Extra       |
+----+-------------+--------+------------+------+----------------+----------------+---------+-------+---------+----------+-------------+
|  1 | SIMPLE      | itest3 | NULL       | ref  | category_index | category_index | 18      | const | 3690834 |    33.33 | Using where |
+----+-------------+--------+------------+------+----------------+----------------+---------+-------+---------+----------+-------------+
1 row in set, 1 warning (0.00 sec)
alter table itest3 add index category_and_num_index(category,num); の後
mysql> explain select count(*) from itest3 where category = "t2";
+----+-------------+--------+------------+------+---------------------------------------+----------------+---------+-------+---------+----------+-------------+
| id | select_type | table  | partitions | type | possible_keys                         | key            | key_len | ref   | rows    | filtered | Extra       |
+----+-------------+--------+------------+------+---------------------------------------+----------------+---------+-------+---------+----------+-------------+
|  1 | SIMPLE      | itest3 | NULL       | ref  | category_index,category_and_num_index | category_index | 18      | const | 3690834 |   100.00 | Using index |
+----+-------------+--------+------------+------+---------------------------------------+----------------+---------+-------+---------+----------+-------------+
1 row in set, 1 warning (0.00 sec)
mysql> explain select count(*) from itest3 where category = "t2" and num > 80;
+----+-------------+--------+------------+-------+---------------------------------------+------------------------+---------+------+--------+----------+--------------------------+
| id | select_type | table  | partitions | type  | possible_keys                         | key                    | key_len | ref  | rows   | filtered | Extra                    |
+----+-------------+--------+------------+-------+---------------------------------------+------------------------+---------+------+--------+----------+--------------------------+
|  1 | SIMPLE      | itest3 | NULL       | range | category_index,category_and_num_index | category_and_num_index | 23      | NULL | 775046 |   100.00 | Using where; Using index |
+----+-------------+--------+------------+-------+---------------------------------------+------------------------+---------+------+--------+----------+--------------------------+
1 row in set, 1 warning (0.00 sec)
(何故1つ目のindex で2番目のクエリがこんなに遅くなったのかがよく分からない……)
まとめ
- 行いたい検索に合わせて使うカラムを指定したindex を貼ると早くなる。
- 適当なindex では逆に遅くなるパターンも。
- index を貼る前に、そのテーブルに対してどのような検索を行いたいかを考える。
- 実際に使っているテーブルに対してindex を貼ったせいで遅くなったりしたら困るので、もうちょい勉強した方が良さげ。
参考
MySQLのインデックスの効果を理解する
MySQLコマンドやSQL文に関する各種メモ書き
MySQL初級者を脱するために勉強してること -INDEX編-
MySQLのExplainを確認する