footmark

ひよっこエンジニアの足跡

【MySQL】オンメモリで動かしてみる2

前回(【MySQL】オンメモリで動かしてみる - footmark)の結果に差が出なかったことに関して、SQL文に問題があったのではと思い、INNER JOIN を使ったSQL文を用意していろいろ弄ってみました。
話が前後しますが、前回のプログラムではwhere句を用いたselect文しか使っていないので、少し処理を重くしてみようということです。

とりあえず実行。

# MyISAM
mysql> select * from doc_word_table inner join word_table on doc_word_table.word_id = word_table.word_id limit 100000;
100000 rows in set (0.38 sec)

# MEMORY
mysql> select * from doc_word_table_memory inner join word_table_memory on doc_word_table_memory.word_id = word_table_memory.word_id limit 100000;
100000 rows in set (5.70 sec)

MEMORYストレージエンジン側がめっちゃ遅い!なんで!?
explain句を使って内部でどんな処理をしているのか見てみます。

# MyISAM
mysql> explain select * from doc_word_table inner join word_table on doc_word_table.word_id = word_table.word_id;
+----+-------------+----------------+--------+---------------+---------+---------+-------------------------------+----------+-------+
| id | select_type | table          | type   | possible_keys | key     | key_len | ref                           | rows     | Extra |
+----+-------------+----------------+--------+---------------+---------+---------+-------------------------------+----------+-------+
|  1 | SIMPLE      | doc_word_table | ALL    | NULL          | NULL    | NULL    | NULL                          | 16358934 |       |
|  1 | SIMPLE      | word_table     | eq_ref | PRIMARY       | PRIMARY | 4       | db.doc_word_table.word_id |        1 |       |
+----+-------------+----------------+--------+---------------+---------+---------+-------------------------------+----------+-------+
2 rows in set (0.00 sec)

# MEMORY
mysql> explain select * from doc_word_table_memory inner join word_table_memory on doc_word_table_memory.word_id = word_table_memory.word_id;
+----+-------------+-----------------------+------+---------------+------+---------+------+----------+--------------------------------+
| id | select_type | table                 | type | possible_keys | key  | key_len | ref  | rows     | Extra                          |
+----+-------------+-----------------------+------+---------------+------+---------+------+----------+--------------------------------+
|  1 | SIMPLE      | word_table_memory     | ALL  | NULL          | NULL | NULL    | NULL |    68058 |                                |
|  1 | SIMPLE      | doc_word_table_memory | ALL  | NULL          | NULL | NULL    | NULL | 16358934 | Using where; Using join buffer |
+----+-------------+-----------------------+------+---------------+------+---------+------+----------+--------------------------------+
2 rows in set (0.00 sec)

MEMORYストレージエンジン側は word_table に key を使っていないので毎回全検索をしていますね。
どうやらここがネックっぽいです。
というかそもそもなんで key 使ってないんだ?設定されていない?
確認してみます。

# MyISAM
mysql> show columns from doc_word_table;
+-------------+---------+------+-----+---------+----------------+
| Field       | Type    | Null | Key | Default | Extra          |
+-------------+---------+------+-----+---------+----------------+
| doc_word_id | int(11) | NO   | PRI | NULL    | auto_increment |
| doc_id   | int(11) | NO   | MUL | NULL    |                |
| word_id     | int(11) | NO   |     | NULL    |                |
+-------------+---------+------+-----+---------+----------------+
3 rows in set (0.00 sec)

# MEMORY
mysql> show columns from doc_word_table_memory;
+-------------+---------+------+-----+---------+-------+
| Field       | Type    | Null | Key | Default | Extra |
+-------------+---------+------+-----+---------+-------+
| doc_word_id | int(11) | NO   |     | 0       |       |
| doc_id   | int(11) | NO   | MUL | NULL    |       |
| word_id     | int(11) | NO   |     | NULL    |       |
+-------------+---------+------+-----+---------+-------+
3 rows in set (0.00 sec)

あれ、主キーが設定されてない。
なるほど、前回MyISAMストレージエンジンのテーブルからコピーしてくるときに設定し忘れていたようです。
というわけで、主キーを貼る。

mysql> alter table doc_word_table_memory add primary key (doc_word_id);
Query OK, 16358934 rows affected (21.18 sec)
Records: 16358934  Duplicates: 0  Warnings: 0

再びexplain句で調べてみます。

mysql> explain select * from doc_word_table_memory inner join word_table_memory on doc_word_table_memory.word_id = word_table_memory.word_id;
+----+-------------+-----------------------+--------+---------------+---------+---------+--------------------------------------+----------+-------+
| id | select_type | table                 | type   | possible_keys | key     | key_len | ref                                  | rows     | Extra |
+----+-------------+-----------------------+--------+---------------+---------+---------+--------------------------------------+----------+-------+
|  1 | SIMPLE      | doc_word_table_memory | ALL    | NULL          | NULL    | NULL    | NULL                                 | 16358934 |       |
|  1 | SIMPLE      | word_table_memory     | eq_ref | PRIMARY       | PRIMARY | 4       | db.doc_word_table_memory.word_id |        1 |       |
+----+-------------+-----------------------+--------+---------------+---------+---------+--------------------------------------+----------+-------+
2 rows in set (0.00 sec)

MyISAMストレージエンジンのそれと同じになりました。
改めてSQL文を打ってみます。

mysql> select * from doc_word_table_memory inner join word_table_memory on doc_word_table_memory.word_id = word_table_memory.word_id limit 100000;
100000 rows in set (0.07 sec)

はええ。
やっぱりMEMORYストレージエンジンさんは速かったんや。

(前回の結果が同じだったのも主キーを貼り忘れたせいじゃね?と思い、前回のプログラムを再実行してみたのですが、結果は変わらずでした。)

考察

前回のプログラム中のSQL文はもともと処理が軽く、メモリ云々では速くしようがない、というのが僕の考察です。

ちなみに、

MyISAM : インデックスはMySQLがキャッシュ管理する(key_buffer_sizeで大きさを設定)。データはOSのキャッシュ機構におまかせ

MyISAMを使っている時key_buffer_sizeは大きくし過ぎてもいけない | b.l0g.jp

なる仕様だそうで、プラス、動かした環境はメモリを128GB積んでいるサーバだったこともあり、差が出なかったのにはOSの仕事っぷりも一枚噛んでいるかもしれません。