当表有其他字段时,Mysql不使用DATETIME索引

时间:2011-04-06 15:29:42

标签: mysql optimization indexing innodb

我需要一些帮助来解决这个问题。我正在尝试让Mysql在DATETIME字段上使用索引。

如果表中有其他(未使用)字段,则Mysql决定不使用索引。请考虑以下两种情况:

包含2个字段的简单表格可以正常使用

DROP TABLE IF EXISTS datetime_index_test;
CREATE TABLE  datetime_index_test (
id INT UNSIGNED NOT NULL AUTO_INCREMENT ,
created DATETIME NOT NULL ,
PRIMARY KEY (id) ,
INDEX (created)
) ENGINE = InnoDB ;

INSERT INTO datetime_index_test (created) VALUES
('2011-04-06 00:00:00'),
('2011-04-06 01:00:00'),
('2011-04-06 02:00:00'),
('2011-04-06 03:00:00'),
('2011-04-06 04:00:00'),
('2011-04-06 05:00:00'),
('2011-04-06 06:00:00'),
('2011-04-06 00:00:00');

EXPLAIN SELECT * FROM datetime_index_test
WHERE created <= '2011-04-06 04:00:00';

+----+-------------+---------------------+-------+---------------+---------+---------+------+------+--------------------------+
| id | select_type | table               | type  | possible_keys | key     | key_len | ref  | rows | Extra                    |
+----+-------------+---------------------+-------+---------------+---------+---------+------+------+--------------------------+
|  1 | SIMPLE      | datetime_index_test | range | created       | created | 4       | NULL |    4 | Using where; Using index |
+----+-------------+---------------------+-------+---------------+---------+---------+------+------+--------------------------+

包含3个字段的简单表格无效

DROP TABLE IF EXISTS datetime_index_test;
CREATE TABLE  datetime_index_test (
id INT UNSIGNED NOT NULL AUTO_INCREMENT ,
created DATETIME NOT NULL ,
user int(10) unsigned DEFAULT 0,
PRIMARY KEY (id) ,
INDEX (created)
) ENGINE = InnoDB ;

INSERT INTO datetime_index_test (created) VALUES
('2011-04-06 00:00:00'),
('2011-04-06 01:00:00'),
('2011-04-06 02:00:00'),
('2011-04-06 03:00:00'),
('2011-04-06 04:00:00'),
('2011-04-06 05:00:00'),
('2011-04-06 06:00:00'),
('2011-04-06 00:00:00');

EXPLAIN SELECT * FROM datetime_index_test
WHERE created <= '2011-04-06 04:00:00';

+----+-------------+---------------------+------+---------------+------+---------+------+------+-------------+
| id | select_type | table               | type | possible_keys | key  | key_len | ref  | rows | Extra       |
+----+-------------+---------------------+------+---------------+------+---------+------+------+-------------+
|  1 | SIMPLE      | datetime_index_test | ALL  | created       | NULL | NULL    | NULL |    8 | Using where |
+----+-------------+---------------------+------+---------------+------+---------+------+------+-------------+

最后,我的问题; 有人可以向我解释为什么Mysql决定不使用索引吗?

1 个答案:

答案 0 :(得分:4)

这是由于我称之为基于关键人口(元组基数)的5%规则。

如果索引存在lopsided基数的表,MySQL Query Optimizer将始终选择阻力最小的路径。

示例:如果某个表包含性别列,则基数为2,即M和F.

你索引这样一个性别列是什么?你本可以得到两个巨大的链表。

如果您将一百万行加载到包含性别列的表格中,则可能会获得50%的M和50%的F。

如果关键组合的基数(我所说的关键字符数)超过总表数的5%,则在查询优化期间索引将变得无用。

现在,关于你的例子,为什么两个不同的EXPLAIN计划???我的猜测是MySQL查询优化器和InnoDB作为标签团队。

在第一个CREATE TABLE中,表和索引的大小差不多,所以它决定通过索引扫描而不是全表扫描来支持索引。请记住,非唯一索引在其索引条目中携带每行的内部主键(RowID),从而使索引的大小与表本身几乎相同。

在第二个CREATE TABLE中,由于引入了另一个列,用户,您现在使查询优化器看到一个完全不同的场景:该表现在大于索引。因此,查询优化器在解释如何使用可用索引时变得更加严格。它达到了我之前提到的5%规则。该规则失败了,查询优化器决定支持全表扫描。