MySQLのインデックスマージを試してみる

MySQLで大量のデータを追加してみる
の続き

インデックスのマージを試してみる。

今回使用するテーブル

今回は下記の構造のテーブルを使用する。

mysql> create table sample (
  id      int         NOT NULL AUTO_INCREMENT,
  c1      int         NOT NULL,
  c2      int         NOT NULL,
  PRIMARY KEY (id)
) ENGINE=InnoDB;

結果を先にまとめると

長くなるので、先に結果をまとめると、下記の通り。
カラムの選択性によって、インデックスマージの効果が違いました。

選択性が高い場合
パターン index(c1)のみ index(c1)とindex(c2)
c1 = N and c2 = N 約0秒 約0秒
c1 = N or c2 = N 約3秒 約0秒

選択性が低い場合
パターン index(c1)のみ index(c1)とindex(c2)
c1 = N and c2 = N 約22秒 約1秒
c1 = N or c2 = N 約3秒 約25秒


選択性が高い場合は、インデックスマージはorの検索に有効だが、
選択性が低い場合は、インデックスマージはorの検索に逆効果っぽい。

とりあえず、よく分からん。


以下、確認した手順と結果を載せておきます。

選択性が高い場合

まずは、sampleテーブルに下記のSQLで1000万件のデータをINSERTする。

mysql> insert into sample (c1, c2)
 select
  FLOOR(RAND()*10000000 + 1),
  FLOOR(RAND()*10000000 + 1)
 from tmp t1, tmp t2, tmp t3, tmp t4, tmp t5, tmp t6, tmp t7;

c1,c2には1〜1000万の値が入り、選択性が高い状態になっている。

index(c1)のみ

まずは、c1のインデックスを作成する。

mysql> alter table sample add idx1 (c1);

この状態で、c1とc2をandで検索する。結果は下記の通り。

mysql> select count(*) from sample where c1 = 100 and c2 = 100;
+----------+
| count(*) |
+----------+
|        0 |
+----------+
1 row in set (0.00 sec)
mysql> explain select count(*) from sample where c1 = 100 and c2 = 100;
+----+-------------+--------+------+---------------+------+---------+-------+------+-------------+
| id | select_type | table  | type | possible_keys | key  | key_len | ref   | rows | Extra       |
+----+-------------+--------+------+---------------+------+---------+-------+------+-------------+
|  1 | SIMPLE      | sample | ref  | idx1          | idx1 | 4       | const |    3 | Using where |
+----+-------------+--------+------+---------------+------+---------+-------+------+-------------+

次に、c1とc2をorで検索する。結果は下記の通り。

mysql> select count(*) from sample where c1 = 100 or c2 = 100;
+----------+
| count(*) |
+----------+
|        5 |
+----------+
1 row in set (3.70 sec)
mysql> explain select count(*) from sample where c1 = 100 or c2 = 100;
+----+-------------+--------+------+---------------+------+---------+------+----------+-------------+
| id | select_type | table  | type | possible_keys | key  | key_len | ref  | rows     | Extra       |
+----+-------------+--------+------+---------------+------+---------+------+----------+-------------+
|  1 | SIMPLE      | sample | ALL  | idx1          | NULL | NULL    | NULL | 10000597 | Using where |
+----+-------------+--------+------+---------------+------+---------+------+----------+-------------+
index(c1)+index(c2)

次に、c2のインデックスを追加して、index(c1)とindex(c2)がある状態にする。

mysql> alter table sample add idx2 (c2);

この状態で、c1とc2をandで検索する。結果は下記の通り。

mysql> select count(*) from sample where c1 = 100 and c2 = 100;
+----------+
| count(*) |
+----------+
|        0 |
+----------+
1 row in set (0.00 sec)
mysql> explain select count(*) from sample where c1 = 100 and c2 = 100;
+----+-------------+--------+-------------+---------------+-----------+---------+------+------+------------------------------------------------------+
| id | select_type | table  | type        | possible_keys | key       | key_len | ref  | rows | Extra                                                |
+----+-------------+--------+-------------+---------------+-----------+---------+------+------+------------------------------------------------------+
|  1 | SIMPLE      | sample | index_merge | idx1,idx2     | idx2,idx1 | 4,4     | NULL |    1 | Using intersect(idx2,idx1); Using where; Using index |
+----+-------------+--------+-------------+---------------+-----------+---------+------+------+------------------------------------------------------+

次に、c1とc2をorで検索する。結果は下記の通り。

mysql> select count(*) from sample where c1 = 100 or c2 = 100;
+----------+
| count(*) |
+----------+
|        5 |
+----------+
1 row in set (0.00 sec)
mysql> explain select count(*) from sample where c1 = 100 or c2 = 100;
+----+-------------+--------+-------------+---------------+-----------+---------+------+------+-------------------------------------+
| id | select_type | table  | type        | possible_keys | key       | key_len | ref  | rows | Extra                               |
+----+-------------+--------+-------------+---------------+-----------+---------+------+------+-------------------------------------+
|  1 | SIMPLE      | sample | index_merge | idx1,idx2     | idx1,idx2 | 4,4     | NULL |    5 | Using union(idx1,idx2); Using where |
+----+-------------+--------+-------------+---------------+-----------+---------+------+------+-------------------------------------+

テーブルをリセット

一旦テーブルを再作成する。

mysql> drop table sample;
mysql> create table sample (・・・);

選択性が低い場合

まずは、sampleテーブルに下記のSQLで1000万件のデータをINSERTする。

mysql> insert into sample (c1, c2)
 select
  FLOOR(RAND()*10 + 1),
  FLOOR(RAND()*10 + 1)
 from tmp t1, tmp t2, tmp t3, tmp t4, tmp t5, tmp t6, tmp t7;

c1,c2には1〜10の値が入り、選択性が低い状態になっている。

index(c1)のみ

まずは、c1のインデックスを作成する。

mysql> alter table sample add idx1 (c1);

この状態で、c1とc2をandで検索する。結果は下記の通り。

mysql> select count(*) from sample where c1 = 10 and c2 = 10;
+----------+
| count(*) |
+----------+
|    99686 |
+----------+
1 row in set (22.03 sec)
>|mysql|
mysql> explain select count(*) from sample where c1 = 10 and c2 = 10;
+----+-------------+--------+------+---------------+------+---------+-------+--------+-------------+
| id | select_type | table  | type | possible_keys | key  | key_len | ref   | rows   | Extra       |
+----+-------------+--------+------+---------------+------+---------+-------+--------+-------------+
|  1 | SIMPLE      | sample | ref  | idx1          | idx1 | 4       | const | 968448 | Using where |
+----+-------------+--------+------+---------------+------+---------+-------+--------+-------------+

次に、c1とc2をorで検索する。結果は下記の通り。

mysql> select count(*) from sample where c1 = 10 or c2 = 10;
+----------+
| count(*) |
+----------+
|  1900349 |
+----------+
1 row in set (3.54 sec)
mysql> explain select count(*) from sample where c1 = 10 or c2 = 10;
+----+-------------+--------+------+---------------+------+---------+------+----------+-------------+
| id | select_type | table  | type | possible_keys | key  | key_len | ref  | rows     | Extra       |
+----+-------------+--------+------+---------------+------+---------+------+----------+-------------+
|  1 | SIMPLE      | sample | ALL  | idx1          | NULL | NULL    | NULL | 10000673 | Using where |
+----+-------------+--------+------+---------------+------+---------+------+----------+-------------+
index(c1)+index(c2)

次に、c2のインデックスを追加して、index(c1)とindex(c2)がある状態にする。

mysql> alter table sample add idx2 (c2);

この状態で、c1とc2をandで検索する。結果は下記の通り。

mysql> select count(*) from sample where c1 = 10 and c2 = 10;
+----------+
| count(*) |
+----------+
|    99686 |
+----------+
1 row in set (0.70 sec)
mysql> explain select count(*) from sample where c1 = 10 and c2 = 10;
+----+-------------+--------+-------------+---------------+-----------+---------+------+-------+------------------------------------------------------+
| id | select_type | table  | type        | possible_keys | key       | key_len | ref  | rows  | Extra                                                |
+----+-------------+--------+-------------+---------------+-----------+---------+------+-------+------------------------------------------------------+
|  1 | SIMPLE      | sample | index_merge | idx1,idx2     | idx2,idx1 | 4,4     | NULL | 39561 | Using intersect(idx2,idx1); Using where; Using index |
+----+-------------+--------+-------------+---------------+-----------+---------+------+-------+------------------------------------------------------+

次に、c1とc2をorで検索する。結果は下記の通り。

mysql> select count(*) from sample where c1 = 10 or c2 = 10;
+----------+
| count(*) |
+----------+
|  1900349 |
+----------+
1 row in set (25.09 sec)
mysql> explain select count(*) from sample where c1 = 10 or c2 = 10;
+----+-------------+--------+-------------+---------------+-----------+---------+------+---------+-------------------------------------+
| id | select_type | table  | type        | possible_keys | key       | key_len | ref  | rows    | Extra                               |
+----+-------------+--------+-------------+---------------+-----------+---------+------+---------+-------------------------------------+
|  1 | SIMPLE      | sample | index_merge | idx1,idx2     | idx1,idx2 | 4,4     | NULL | 1376960 | Using union(idx1,idx2); Using where |
+----+-------------+--------+-------------+---------------+-----------+---------+------+---------+-------------------------------------+
【参考】
MySQL INDEX+EXPLAIN入門
http://www.slideshare.net/infinite_loop/mysql-indexexplain