MySQL 5.0.77 で実行されているアプリケーションに、大きくて急速に成長しているログ テーブルがあります。メッセージの種類に応じて、過去 X 日間のインスタンスをカウントするクエリを最適化する最善の方法を見つけようとしています。
CREATE TABLE `counters` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`kind` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL,
`created_at` datetime DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `index_counters_on_kind` (`kind`),
KEY `index_counters_on_created_at` (`created_at`)
) ENGINE=InnoDB AUTO_INCREMENT=302 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
このテスト セットでは、テーブルに 668521 行あります。最適化しようとしているクエリは次のとおりです。
SELECT kind, COUNT(id) FROM counters WHERE created_at >= ? GROUP BY kind;
現在、そのクエリには 3 ~ 5 秒かかり、次のように見積もられています。
+----+-------------+----------+-------+----------------------------------+------------------------+---------+------+---------+-------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+----------+-------+----------------------------------+------------------------+---------+------+---------+-------------+
| 1 | SIMPLE | counters | index | index_counters_on_created_at_idx | index_counters_on_kind | 258 | NULL | 1185531 | Using where |
+----+-------------+----------+-------+----------------------------------+------------------------+---------+------+---------+-------------+
1 row in set (0.00 sec)
created_at インデックスを削除すると、次のようになります。
+----+-------------+----------+-------+---------------+------------------------+---------+------+---------+-------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+----------+-------+---------------+------------------------+---------+------+---------+-------------+
| 1 | SIMPLE | counters | index | NULL | index_counters_on_kind | 258 | NULL | 1185531 | Using where |
+----+-------------+----------+-------+---------------+------------------------+---------+------+---------+-------------+
1 row in set (0.00 sec)
(はい、何らかの理由で、行の見積もりがテーブル内の行数よりも大きくなっています。)
したがって、明らかに、そのインデックスには意味がありません。
これを行うより良い方法は本当にありませんか?列をタイムスタンプとして試しましたが、遅くなりました。
編集:特定の日付の代わりに間隔を使用するようにクエリを変更すると、最終的にインデックスが使用され、行の見積もりが上記のクエリの約 20% に削減されることがわかりました。
SELECT kind, COUNT(id) FROM counters WHERE created_at >=
(NOW() - INTERVAL 7 DAY) GROUP BY kind;
なぜそうなったのかは完全にはわかりませんが、それを理解すれば、問題は一般的にもっと理にかなっているだろうと確信しています.