MySQL でスロークエリログを出力するようにしたところ、いくつかのクエリが遅いようだった。
中でも次の非常に単純なクエリが遅いのが気になった。
しかし、クエリはこれ以上改良しようがない。
結論を言えば「MySQL の主キーのスキャンはテーブルスキャンなので遅い。セカンダリーインデックスを設定すればインデックスのみのスキャンになるので速くなる。」ということだ。
http://nippondanji.blogspot.jp/2010/03/innodbcount.html
また、MySQL の explain コマンドについても同サイトで詳しく解説されている。
http://nippondanji.blogspot.jp/2009/03/mysqlexplain.html
セカンダリーインデックスを設定する前。
key のところを見ると PRIMARY となっているのが分かる。
セカンダリーインデックスを設定する。
key のところが設定したばかりのセカンダリーインデックスに変わった。
そして問題のクエリの実行時間は10分の1程度になった。
中でも次の非常に単純なクエリが遅いのが気になった。
しかし、クエリはこれ以上改良しようがない。
mysql> select TradingDay from test_db.table1 -> group by TradingDay order by TradingDay desc limit 10; +---------------------+ | TradingDay | +---------------------+ | 2013-06-25 00:00:00 | | 2013-06-07 00:00:00 | | 2013-06-06 00:00:00 | | 2013-06-05 00:00:00 | | 2013-06-04 00:00:00 | | 2013-06-03 00:00:00 | | 2013-05-31 00:00:00 | | 2013-05-30 00:00:00 | | 2013-05-29 00:00:00 | | 2013-05-28 00:00:00 | +---------------------+ 10 rows in set (0.45 sec)テーブルの定義は以下のようなもので、InnoDB を使っている。
CREATE TABLE `table1` ( `TradingDay` datetime NOT NULL, `BrandCode` varchar(10) NOT NULL COMMENT '銘柄コード', `BrandName` varchar(45) DEFAULT NULL COMMENT '銘柄名', `MarketCode` varchar(4) DEFAULT NULL COMMENT '市場コード', `Characteristics` varchar(200) DEFAULT NULL COMMENT '特色', `CreateTime` datetime DEFAULT NULL COMMENT '作成日時', `ModifiedTime` datetime DEFAULT NULL COMMENT '更新日時', PRIMARY KEY (`BrandCode`,`TradingDay`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='銘柄テーブル'$$レコード数は42万件。けっして少なくはないが。。。
mysql> SELECT count(*) FROM test_db.table1; +----------+ | count(*) | +----------+ | 425496 | +----------+ 1 row in set (0.17 sec)遅い理由については以下のページに詳しい解説があった。
結論を言えば「MySQL の主キーのスキャンはテーブルスキャンなので遅い。セカンダリーインデックスを設定すればインデックスのみのスキャンになるので速くなる。」ということだ。
http://nippondanji.blogspot.jp/2010/03/innodbcount.html
また、MySQL の explain コマンドについても同サイトで詳しく解説されている。
http://nippondanji.blogspot.jp/2009/03/mysqlexplain.html
セカンダリーインデックスを設定する前。
key のところを見ると PRIMARY となっているのが分かる。
mysql> explain -> select TradingDay from test_db.table1 -> group by TradingDay order by TradingDay desc limit 10; +----+-------------+--------+-------+---------------+---------+---------+------+--------+----------------------------------------------+ | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | +----+-------------+--------+-------+---------------+---------+---------+------+--------+----------------------------------------------+ | 1 | SIMPLE | table1 | index | NULL | PRIMARY | 40 | NULL | 418139 | Using index; Using temporary; Using filesort | +----+-------------+--------+-------+---------------+---------+---------+------+--------+----------------------------------------------+ 1 row in set (0.00 sec)
mysql> ALTER TABLE test_db.table1 ADD INDEX (TradingDay); Query OK, 0 rows affected (7.33 sec) Records: 0 Duplicates: 0 Warnings: 0セカンダリーインデックスを設定した後。
key のところが設定したばかりのセカンダリーインデックスに変わった。
mysql> explain -> select TradingDay from test_db.table1 -> group by TradingDay order by TradingDay desc limit 10; +----+-------------+--------+-------+---------------+------------+---------+------+--------+-------------+ | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | +----+-------------+--------+-------+---------------+------------+---------+------+--------+-------------+ | 1 | SIMPLE | table1 | index | NULL | TradingDay | 8 | NULL | 418139 | Using index | +----+-------------+--------+-------+---------------+------------+---------+------+--------+-------------+ 1 row in set (0.00 sec)
mysql> select TradingDay from test_db.table1 -> group by TradingDay order by TradingDay desc limit 10; +---------------------+ | TradingDay | +---------------------+ | 2013-06-25 00:00:00 | | 2013-06-07 00:00:00 | | 2013-06-06 00:00:00 | | 2013-06-05 00:00:00 | | 2013-06-04 00:00:00 | | 2013-06-03 00:00:00 | | 2013-05-31 00:00:00 | | 2013-05-30 00:00:00 | | 2013-05-29 00:00:00 | | 2013-05-28 00:00:00 | +---------------------+ 10 rows in set (0.03 sec)以上。