MySQL对varchar字段的部分索引和按优化分组

时间:2011-04-07 22:10:49

标签: mysql group-by indexing

我在使用MySQL进行群组查询时遇到了一些问题。

问题

有没有理由为什么查询不会在varchar(255)字段上使用10个字符的部分索引来优化组?

详情

我的设置:

CREATE TABLE `sessions` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `user_id` int(11) DEFAULT NULL,
  `ref_source` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL,
  `guid` varchar(255) COLLATE utf8_unicode_ci NOT NULL,
  `initial_path` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL,
  `referrer_host` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL,
  `campaign` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL,
  PRIMARY KEY (`id`),
  KEY `index_sessions_on_user_id` (`user_id`),
  KEY `index_sessions_on_referrer_host` (`referrer_host`(10)),
  KEY `index_sessions_on_initial_path` (`initial_path`(10)),
  KEY `index_sessions_on_campaign` (`campaign`(10))
) ENGINE=InnoDB AUTO_INCREMENT=0 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci

此处未显示多个列和索引,因为它们并未真正影响该问题。

我想要做的是运行查询以查看所有引用主机以及来自每个主机的会话数。我没有一张大桌子,但它足够大,我全桌扫描并不好玩。我想要运行的查询是:

SELECT COUNT(*) AS count_all, referrer_host AS referrer_host FROM `sessions` GROUP BY referrer_host;

解释给出:

+----+-------------+----------+------+---------------+------+---------+------+--------+---------------------------------+
| id | select_type | table    | type | possible_keys | key  | key_len | ref  | rows   | Extra                           |
+----+-------------+----------+------+---------------+------+---------+------+--------+---------------------------------+
|  1 | SIMPLE      | sessions | ALL  | NULL          | NULL | NULL    | NULL | 303049 | Using temporary; Using filesort |
+----+-------------+----------+------+---------------+------+---------+------+--------+---------------------------------+

我在referrer_host上有部分索引,但它没有使用它。即使我尝试USE INDEXFORCE INDEX,也无济于事。解释与表现一样。

如果我在referrer_host上添加完整索引,而不是10个字符的部分索引,那么一切都会更好,如果不是立即的话。 (350毫秒对10秒)

我已经测试了比该字段中最长条目更大的部分索引,但也无济于事。完整索引是唯一可行的方法。

3 个答案:

答案 0 :(得分:1)

尝试此查询:

EXPLAIN SELECT COUNT(referrer_host) AS count_all, referrer_host  FROM `sessions` GROUP BY referrer_host;

现在,对于该组的计数将在referrer_host = null上失败,但我不确定是否还有另一种解决方法。

答案 1 :(得分:1)

您正在为referrer_host分组表中的所有行。由于您的索引不包含referrer_host(它包含前10个字符!),它将扫描整个表。

我敢打赌,这更快,但不太详细:

SELECT COUNT(*) AS count_all, substring(referrer_host,1,10) AS referrer_host FROM `sessions` GROUP BY referrer_host;

如果您需要完整的推荐人,请将其编入索引。

答案 2 :(得分:1)

使用完整索引,查询将查找扫描整个索引并返回为每个唯一键指向的记录数。表格没有被触及。

使用部分索引,引擎在查看记录之前不知道referrer_host的值。它必须扫描整个表格!

如果referrer_host的大多数值小于10个字符,那么理论上,优化器可以使用索引,然后只检查具有10个以上字符的行。但是,因为这不是聚簇索引,所以必须进行许多非顺序磁盘读取才能找到这些记录。最终可能会更慢,因为表扫描至少是顺序读取。优化器只是进行扫描而不是做出假设。