mysql创建的索引不生效,查询数据仍是全表扫描?

本文涉及的产品
RDS MySQL DuckDB 分析主实例,集群系列 4核8GB
RDS AI 助手,专业版
简介: mysql创建的索引不生效,查询数据仍是全表扫描?

一年前,有人问我,为什么我创建了索引,然后用这个字段做查询的时候还是全表扫描?
如题:

  • 建表语句
-- auto-generated definition
create table t_test
(
    id    int auto_increment
        primary key,
    name  varchar(10)  null,
    hobby varchar(255) null
);

create index t_test_pk_2
    on t_test (hobby);
  • 测试数据
INSERT INTO `wechat_article`.`t_test`(`name`, `hobby`) VALUES ('小朱', '玩游戏');
INSERT INTO `wechat_article`.`t_test`(`name`, `hobby`) VALUES ('小王', '学习');
INSERT INTO `wechat_article`.`t_test`(`name`, `hobby`) VALUES ('小单', '玩游戏');
INSERT INTO `wechat_article`.`t_test`(`name`, `hobby`) VALUES ('小强', '看书');
INSERT INTO `wechat_article`.`t_test`(`name`, `hobby`) VALUES ('小祝', '玩游戏');
id name hobby
1 小朱 玩游戏
2 小王 学习
3 小单 玩游戏
4 小强 看书
5 小祝 玩游戏
select * from t_test where hobby = '玩游戏'

这条语句走没走索引!

我觉得吧,会有 90% 的人会觉的走索引吧!

不论怎么样我们用 explain 查一下看看

explain select * from t_test where hobby = '玩游戏';

执行结果

id select_type table partitions type possible_keys key key_len ref rows filtered Extra
1 SIMPLE t_test ALL t_test_pk_2 5 60 Using where

为了照顾有的同学不知道上面的每列的意思,下面我就介绍下每列吧。

id

编号,没什么好讲的。

select_type

查询的类别,是简单的还是复杂的。复杂的又分为3种。

  • SIMPLE:表示简单的 select ,没有 union 和子查询
  • PRIMARY:最外面的查询 或者 主查询,在有子查询的语句中,最外面的 select 查询就是PRIMARY
  • SUBQUERY:子查询
  • UNION:UNION语句的第二个或者说是后面那一个 select
  • UNION RESULTt:UNION 之后的结果
  • DEPENDENT UNOIN:UNOIN 中的第二个或随后的 select 查询,依赖于外部查询的结果集
  • DEPENDENT SUBQUERY:子查询中的第一个 select 查询,依赖于外部 查询的结果集
  • DERIVED:衍生表

table

表的名称,或者表的别名。

partitions

使用的哪些分区(对于非分区表值为null)

type

  • const:表中最多有一个匹配行,const用于比较primary key 或者unique索引。因为只匹配一行数据,所以很快
  • eq_ref:唯一性索引扫描,对于每个来自于前面的表的记录,从该表中读取唯一一行
  • ref:非唯一性索引扫描,对于每个来自于前面的表的记录,所有匹配的行从这张表取出
  • ref_or_null:类似于ref,但是可以搜索包含null值的行,例如:select * from student where address='xxx' or address is null,需要在 address 建立索引。
  • index_merge:查询语句用到了一张表的多个索引时,mysql会将多个索引合并到一起
  • range:按指定范围(如in、<、>、between and等,但是前提是此字段要建立索引)来检索,很常见。如:select * from student where id < 5,id上要有索引。
  • index:全”表“扫描,但是是在索引树中扫描,通常比 ALL 快,因为索引文件通常比数据文件小,index 扫描是通过二叉树的方式扫描,而 All 是扫描物理表。(也就是说虽然all和index都是读全表,但index是从索引中读取的,而all是从硬盘中读的)。例如:select name from student,但name字段上需要建立索引,也就是查询的字段属于索引中的字段。
  • ALL:全表扫描,扫描完整的物理表,此时就需要优化了。

possible_keys

指出 MySQL 能在该表中可能使用的索引,显示的是索引的名称,多个索引用逗号隔开,如果没有,则为 null 。

key

MySQL决定实际用到的索引,显示的是索引的名称,多个索引用逗号隔开,如果没有,则为 null 。

key_len

key_len 只计算 where 条件用到的索引长度,而排序和分组就算用到了索引,也不会计算到 key_len 中。

ref

显示索引的哪一列被使用了,如果可能的话,是一个常数

rows

显示mysql认为执行查询时必须要返回的行数,可结合 type 和 key 分析,没有用上索引的情况下,会全表扫描。rows 的值越小越好,说明检索的数据少

filtered

给出了一个百分比的值,这个百分比值和rows列的值一起使用,可以估计出那些将要和执行计划中的前一个表(前一个表就是指id列的值比当前表的id小的表)进行连接的行的数目。

extra

  • using where:表示查询使用了 where 语句来处理结果
  • using index:表示使用了覆盖索引。
  • using join buffer:这个值强调了在获取连接条件时没有使用索引,并且需要连接缓冲区来存储中间结果。
  • using filesort:这是 order by 语句的结果。
  • using temporary:mysql 需要创建一张临时表来保存中间结果。

每列都介绍完了,上面用 explain 得出来的结果得知, select * from t_test where hobby = '玩游戏' 这个 sql 没走索引而是走的全表扫描。有人就会觉得很好奇,为什么索引失效了呢?

解释这个问题前,我在问个问题,select * from t_test where hobby = '看书' 这条语句是走索引,还是全表扫描呢?有人会觉得应该和上面一样吧,难道走不走索引还和数据有关?

explain select * from t_test where hobby = '学习';

执行结果

id select_type table partitions type possible_keys key key_len ref rows filtered Extra
1 SIMPLE t_test ref t_test_pk_2 t_test_pk_2 1023 const 1 100

从上面可以看出,这条语句走了,非唯一性索引扫描(type = ref)索引是 t_test_pk_2 。

哈哈,还真和数据有关系。我勒个擦擦擦!!!

其实,我们都知道 mysql 在查询时都有个执行计划,执行计划怎么来的呢?

看上图就知道了,有个 查询优化器 MySql 使用基于成本的优化器,它将尝试预测一个查询使用某种执行计划的成本,并选择其中成本最小的一个。也就是说, MySql 会执行自己觉得最优的查询逻辑。如果你并不是数据库大师,那 MySql 的查询优化器就是最好的选择。那为什么数据库会选择走全表扫描呢?为什么数据库觉得走全表扫描,比走索引快呢!

说到这我们就要得聊聊索引方面的知识了,这里我们就只聊聊 聚簇索引和非聚簇索引 吧。

聚簇索引:聚簇索引就是按照每张表的主键构造一颗B+树,同时叶子节点中存放的就是整张表的行记录数据,也将聚集索引的叶子节点称为数据页。这个特性决定了索引组织表中数据也是索引的一部分,每张表只能拥有一个聚簇索引。

非聚簇索引(辅助索引):在聚簇索引之上创建的索引称之为辅助索引,辅助索引访问数据总是需要二次查找。

所以,上面我们在 hobby 上建的索引其实就是辅助索引!也就是说我们用 hobby 索引查找需要二次查找的。也就是说当你获取的数量过大,然后再查找两次的情况下 MySql 会觉得走全表扫描效率会更高。这边在网上找的的说法是,当你查找的数据数据量占比高于 30%-40%(此值只是个模糊值) 以上就会走全表扫描。

所以 MySql 创建的索引并不是不生效,而是它觉得全表扫描都比走你创建的索引快。 哈哈。

ok,结束!突然又想问个问题!

select hobby from t_test where hobby = '玩游戏';

走索引吗?

相关实践学习
每个IT人都想学的“Web应用上云经典架构”实战
本实验从Web应用上云这个最基本的、最普遍的需求出发,帮助IT从业者们通过“阿里云Web应用上云解决方案”,了解一个企业级Web应用上云的常见架构,了解如何构建一个高可用、可扩展的企业级应用架构。
MySQL数据库入门学习
本课程通过最流行的开源数据库MySQL带你了解数据库的世界。 &nbsp; 相关的阿里云产品:云数据库RDS MySQL 版 阿里云关系型数据库RDS(Relational Database Service)是一种稳定可靠、可弹性伸缩的在线数据库服务,提供容灾、备份、恢复、迁移等方面的全套解决方案,彻底解决数据库运维的烦恼。 了解产品详情:&nbsp;https://www.aliyun.com/product/rds/mysql&nbsp;
相关文章
|
4月前
|
SQL 缓存 监控
MySQL缓存机制:查询缓存与缓冲池优化
MySQL缓存机制是提升数据库性能的关键。本文深入解析了MySQL的缓存体系,包括已弃用的查询缓存和核心的InnoDB缓冲池,帮助理解缓存优化原理。通过合理配置,可显著提升数据库性能,甚至达到10倍以上的效果。
|
4月前
|
SQL 关系型数据库 MySQL
MySQL的查询操作语法要点
储存过程(Stored Procedures) 和 函数(Functions) : 储存过程和函数允许用户编写 SQL 脚本执行复杂任务.
261 14
|
4月前
|
SQL 关系型数据库 MySQL
MySQL的查询操作语法要点
以上概述了MySQL 中常见且重要 的几种 SQL 查询及其相关概念 这些知识点对任何希望有效利用 MySQL 进行数据库管理工作者都至关重要
143 15
|
4月前
|
SQL 监控 关系型数据库
SQL优化技巧:让MySQL查询快人一步
本文深入解析了MySQL查询优化的核心技巧,涵盖索引设计、查询重写、分页优化、批量操作、数据类型优化及性能监控等方面,帮助开发者显著提升数据库性能,解决慢查询问题,适用于高并发与大数据场景。
|
4月前
|
SQL 监控 关系型数据库
MySQL高级查询技巧:子查询、联接与集合操作
本文深入解析了MySQL高级查询的核心技术,包括子查询、联接和集合操作,通过实际业务场景展示了其语法、性能差异和适用场景,并提供大量可复用的代码示例,助你从SQL新手进阶为数据操作高手。
|
4月前
|
缓存 关系型数据库 BI
使用MYSQL Report分析数据库性能(下)
使用MYSQL Report分析数据库性能
423 158
|
4月前
|
关系型数据库 MySQL 数据库
自建数据库如何迁移至RDS MySQL实例
数据库迁移是一项复杂且耗时的工程,需考虑数据安全、完整性及业务中断影响。使用阿里云数据传输服务DTS,可快速、平滑完成迁移任务,将应用停机时间降至分钟级。您还可通过全量备份自建数据库并恢复至RDS MySQL实例,实现间接迁移上云。
|
4月前
|
关系型数据库 MySQL 数据库
阿里云数据库RDS费用价格:MySQL、SQL Server、PostgreSQL和MariaDB引擎收费标准
阿里云RDS数据库支持MySQL、SQL Server、PostgreSQL、MariaDB,多种引擎优惠上线!MySQL倚天版88元/年,SQL Server 2核4G仅299元/年,PostgreSQL 227元/年起。高可用、可弹性伸缩,安全稳定。详情见官网活动页。
937 152
|
4月前
|
关系型数据库 MySQL 数据库
阿里云数据库RDS支持MySQL、SQL Server、PostgreSQL和MariaDB引擎
阿里云数据库RDS支持MySQL、SQL Server、PostgreSQL和MariaDB引擎,提供高性价比、稳定安全的云数据库服务,适用于多种行业与业务场景。
780 156
|
4月前
|
缓存 监控 关系型数据库
使用MYSQL Report分析数据库性能(中)
使用MYSQL Report分析数据库性能
392 156

推荐镜像

更多