MySQL索引范围查询失效问题

有一张表user

CREATE TABLE `user` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `name` varchar(20) DEFAULT NULL,
  `age` int(11) DEFAULT NULL,
  `address` varchar(20) DEFAULT NULL,
  PRIMARY KEY (`id`),
  KEY `age` (`age`),
  KEY `age_name` (`age`,`name`)
) ENGINE=InnoDB AUTO_INCREMENT=10019201 DEFAULT CHARSET=utf8mb4

其中age字段上有索引,age最大值为100(年龄范围为1-100),此表有1000w条数据,那么平均每个年龄上都有10w条数据

此表有1000w条数据

mysql> select count(id) from user;
+-----------+
| count(id) |
+-----------+
|  10000000 |
+-----------+

我explain了一条sql语句

explain select * from user where age > 50

本来以为会使用到索引(当然的啊,字段有索引,不使用到才怪呢),可结果并不是这样

explain的结果:

mysql> explain select *  from user where age > 50;
+----+-------------+-------+------------+------+---------------+------+---------+------+---------+----------+-------------+
| id | select_type | table | partitions | type | possible_keys | key  | key_len | ref  | rows    | filtered | Extra       |
+----+-------------+-------+------------+------+---------------+------+---------+------+---------+----------+-------------+
|  1 | SIMPLE      | user  | NULL       | ALL  | age,age_name  | NULL | NULL    | NULL | 9729299 |    50.00 | Using where |
+----+-------------+-------+------------+------+---------------+------+---------+------+---------+----------+-------------+

咦?为何不使用索引,而是全表扫描?,我想了老大一会儿,又explain了一下,这次我缩小age的查找范围

mysql> explain select * from user where id > 90;
+----+-------------+-------+------------+-------+---------------+---------+---------+------+---------+----------+-------------+
| id | select_type | table | partitions | type  | possible_keys | key     | key_len | ref  | rows    | filtered | Extra       |
+----+-------------+-------+------------+-------+---------------+---------+---------+------+---------+----------+-------------+
|  1 | SIMPLE      | user  | NULL       | range | PRIMARY       | PRIMARY | 4       | NULL | 4864649 |   100.00 | Using where |
+----+-------------+-------+------------+-------+---------------+---------+---------+------+---------+----------+-------------+

这次正常了,那我就搞不明白了,本来就头发少的我又隐约看见几根头发落地,为了这几根头发,可一定要知道为什么!!!

经过了一番折腾,我发现只有在age>90这个条件才会用到索引,也就是说查询量为1000w的百分之10,超过此查询量,mysql优化器会将索引查询优化位全表扫描,可是它为什么要将查询快速的索引查询优化为查询缓慢的全表扫描呢?这算哪门子优化?嘿嘿,其实并不是这样,当依靠索引查询量较为庞大时,因为索引指向的数据在磁盘中并不是顺序的,所以会产生大量的随机IO(random disk),而随机IO是非常慢的(限于机械硬盘),与其这样,倒不如直接全表扫描,全表扫描至少还是顺序IO的,在速度上也会比随机IO要快(但不是一定的)

总结:在上面的例子中,我们发现当索引查询的数据量较为庞大时,优化器会将索引查询优化为全表扫描,目的是为了减少数量及其庞大的随机IO,以达到优化的目的,所以解决此问题的关键在于,我们建立索引时尽量不要建立在数据比较集中的字段上(比如年龄,性别)

本人才疏学浅,如有错误,还请指正,如要转载,还请注明出处

猜你喜欢

转载自blog.csdn.net/sjhcqw/article/details/109103008