【发布时间】:2011-12-11 07:22:47
【问题描述】:
我在运行 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;
我不完全确定为什么会发生这种情况,但我相当有信心,如果我理解了它,那么这个问题通常会更有意义。
【问题讨论】:
-
我认为,发生的事情是当您使用特定日期时将其作为字符串输入,这导致 MySQL 将
created_at转换为字符串而不使用索引。你能试试WHERE created_at >= CONVERT(DATETIME, '{specific date}')之类的吗? -
使用(例如)CONVERT('2011-10-13', DATETIME) 时没有变化。
标签: mysql performance datetime indexing database-performance