我有一个包含数百万条记录的表:
CREATE TABLE `customers` (
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
`store_id` int(10) unsigned DEFAULT NULL,
`first_name` varchar(64) DEFAULT NULL,
`middle_name` varchar(64) DEFAULT NULL,
`last_name` varchar(64) DEFAULT NULL,
`email` varchar(128) DEFAULT NULL,
`phone` varchar(20) DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `index_store_email` (`store_id`,`email`),
KEY `index_store_phone` (`store_id`,`phone`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;
查询1大约需要800毫秒:
SELECT COUNT(*) FROM `customers` WHERE `store_id` = 1;
查询2大约需要1.5毫秒:
SELECT COUNT(*) FROM `customers` WHERE `store_id` = 1 AND `email` IS NULL;
查询3竟然需要5秒钟:
SELECT COUNT(*) FROM `customers` WHERE `store_id` = 1 AND `email` IS NOT NULL;
注:
- 我简化了表格以提出问题,但查询是相同的。
- 是的,我的表已经优化过了。
- 是的,这两个字段都有索引,请参考上面的创建语法。
- 只有几个
store_id
s,但每个记录都有一个。 - 有很少客户的
email
为null
。
我发现这里有几件奇怪的事情:
- 查询1是最简单的!只有几个可能的INT值。难道它不应该是最快的吗?
- 为什么查询3这么慢?通过执行其他两个查询并将#1从#2中减去,我可以将时间减半,但我不应该这样做。
对于这个看似基本的问题,你有什么想法?我感觉自己错过了一些简单的东西。我在数据库学校里睡着了吗?
EXPLAIN
吗?我猜测由于表中的数据而变得更慢了。如果只有几行具有email = NULL
,那么它可以轻松使用索引并返回相当快的结果。第一个查询较慢,因为必须遍历许多更多的行,如果只有少量不同的store_id
结果,MySQL可能会决定遍历整个表比遍历索引更快。这可能是错误的选择。 - Wolphrows
。它应该告诉你有多少行被检查以返回结果。 - Wolphpossible_keys
显示出当前查询可能使用的索引,而key
则表示实际使用了哪个索引(如果有的话)。 - Wolph