MySQL中 IN 到底走不走索引?

文章目录


前言

在 MySQL 中,IN 语句是否能够利用索引取决于多个因素,包括但不限于查询的具体形式、表的统计信息、索引的选择度等。以下通过几个案例来帮助理解 IN 语句与索引使用的关系。

数据库表结构

sql 复制代码
CREATE TABLE `device_record_gongdi` (
  `id` varchar(32) NOT NULL COMMENT '主键',
  `device_code` varchar(32) NOT NULL COMMENT '设备code',
  `device_type` tinyint(1) NOT NULL COMMENT '设备类型(0:微站设备 1:工地扬尘监测)',
  `time_type` tinyint(1) DEFAULT NULL COMMENT '时间类型(1:一分钟 2:五分钟 3:一小时)',
  `cn` varchar(4) DEFAULT NULL COMMENT '时间类型编码: 2011 分钟、2051 5分钟、2061 小时',
  `aqi` int(11) DEFAULT NULL,
  `level` varchar(50) DEFAULT NULL COMMENT '等级',
  `pollutions` varchar(255) DEFAULT NULL COMMENT '首要污染物',
  `zhzs` double(11,3) DEFAULT NULL COMMENT '综合指数',
  `pm25_avg` double(6,2) unsigned DEFAULT NULL COMMENT 'PM25 指定时间内平均值',
  `pm10_avg` double(6,2) unsigned DEFAULT NULL COMMENT 'PM10 指定时间内平均值',
  `so2_avg` double(6,2) unsigned DEFAULT NULL COMMENT 'SO2 指定时间内平均值',
  `no2_avg` double(6,2) unsigned DEFAULT NULL COMMENT 'NO2 指定时间内平均值',
  `co_avg` double(6,2) unsigned DEFAULT NULL COMMENT 'CO 指定时间内平均值',
  `data_time` datetime DEFAULT NULL COMMENT '数据采集时间',
  `create_time` datetime DEFAULT NULL COMMENT '创建时间',
  PRIMARY KEY (`id`),
  KEY `gd_data_code_type_time` (`device_code`,`time_type`,`data_time`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 ROW_FORMAT=COMPACT COMMENT='工地数据表';

复合索引(联合索引):(device_code,time_type,data_time)

查询sql

使用EXPLAIN查看是否走索引

sql 复制代码
 EXPLAIN
 SELECT *
 FROM device_record_gongdi
 WHERE device_code in ('ZR802241106062','ZR802240801012');

可以看到没有走索引

EXPLAIN介绍

EXPLAIN 关键字在 MySQL 中是一个非常有用的工具,它可以帮助我们了解 MySQL 如何执行SQL语句。通过使用 EXPLAIN,可以获取 MySQL 执行查询的计划,这包括如何连接表、使用哪些索引、是否进行了文件排序或临时表等详细信息。

EXPLAIN 的输出每列解释

  • id: 表示查询中每个部分的选择标识符。如果查询比较简单,可能只有一个 id。对于复杂查询(如子查询),可能会有多个 id。
  • select_type: 显示对应行是简单还是复杂 SELECT 类型。例如:
    • SIMPLE: 简单查询,不包含子查询或 UNION。
    • PRIMARY: 最外层的查询。
    • SUBQUERY: 子查询中的第一个 SELECT。
    • DERIVED: 派生表(在 FROM 子句中的子查询)。
  • table: 显示该行引用的表名。
  • type: 显示连接类型,从最佳到最差依次为:system, const, eq_ref, ref, range, index, 和 ALL。理想情况下,应尽量避免 ALL 类型,因为它表示全表扫描。
  • possible_keys: 显示 MySQL 在查询过程中可以使用的索引。
  • key: 实际上被 MySQL 选择用来加速查询的索引。如果没有索引被使用,则值为 NULL。
  • key_len: 被选中索引使用的字节数,可以通过这个值来判断是否使用了复合索引的部分字段。
  • ref: 显示与索引比较的列或常量。
  • rows: 估算出找到所需的记录所需要读取的行数,值越小越好
  • Extra: 包含不适合在其他列中显示的额外信息,比如"Using where", "Using index", "Using temporary", "Using filesort"等。

本文主要观察type、key和rows列。

强制走索引

通过使用force index让sql强制走索引

sql 复制代码
 EXPLAIN
 SELECT *
 FROM device_record_gongdi force index (gd_data_code_type_time)
 WHERE device_code in ('ZR802241106062','ZR802240801012');

可以看到强制走索引后,rows值没有减少,仍然为382258行。

继续向下看

查询时添加条件(复合索引字段)

表中time_type为时间类型,1代表一分钟数据,2代表五分钟数据,3代表一小时数据。

表中创建了复合索引:(device_code,time_type,data_time)

查询小时

sql 复制代码
 EXPLAIN
 SELECT *
 FROM device_record_gongdi
 WHERE device_code in ('ZR802241106062','ZR802240801012')
  AND time_type = 3;

可以看到in又走了索引,rows扫描的行数降到了3013

查询分钟

sql 复制代码
 EXPLAIN
 SELECT *
 FROM device_record_gongdi
 WHERE device_code in ('ZR802241106062','ZR802240801012')
  AND time_type = 1;
 EXPLAIN
 SELECT *
 FROM device_record_gongdi force index (gd_data_code_type_time)
 WHERE device_code in ('ZR802241106062','ZR802240801012')
  AND time_type = 1;

重点观察rows扫描的行数

这里可以发现,在执行sql时,in一般是走索引的。只有当mysql的查询优化器认为走索引时也需要扫描大量的数据时,就会变为全表扫描。(有些地方写的是当走索引查询后,rows值在数据表中的占比不超过 30% 时,大概率会走索引)

分别执行这两条sql

从这里也可以看出,当使用索引和不使用索引查询时间相差无几时,in就会不走索引。
注意:查询出的条数不同是因为查询时有数据插入

总结

MySQL 中IN语句不一定会走索引,具体取决于多种因素:

  • IN一般是走索引的。只有当mysql的查询优化器认为走索引时也需要扫描大量的数据时,就会变为全表扫描。
  • 当索引覆盖了IN查询的所有列,即查询所需的数据都能从索引中获取,MySQL 可以使用这个索引来加速查询。
  • IN值列表中的值在索引的前缀位置,MySQL 能够利用索引加速查询。若IN值列表的值不在索引的前缀位置,MySQL 无法借助索引加速查询。
  • 也可以使用EXISTS替代IN,在某些场景下能显著提升查询性能。

相关推荐
csdn_aspnet8 分钟前
用 MySQL 玩转数据可视化:从底层数据到 BI 工具的桥接
数据库·mysql·信息可视化·bi
wb0430720114 分钟前
一次jvm配置问题导致的数据库连接异常
服务器·jvm·数据库·后端
酷酷的崽79814 分钟前
搭载cpolar,让PostgreSQL数据库远程访问超丝滑
数据库·postgresql
API开发21 分钟前
apiSQL 迁移至已有 PostgreSQL 数据库指南
数据库·postgresql·api开发·postgrest·接口开发工具·api管理软件
学掌门24 分钟前
从数据库到可视化性能,5个大数据分析工具测评,python只排倒数
数据库·python·数据分析
编程小风筝25 分钟前
Django REST framework实现安全鉴权机制
数据库·安全·django
secondyoung25 分钟前
队列原理与实现全解析
c语言·数据库·mysql·算法·队列
05大叔31 分钟前
微服务Day01 MP
数据库·oracle
Jan123.34 分钟前
深入理解数据库事务与锁机制:InnoDB实战指南
数据库·学习
wWYy.34 分钟前
详解redis(6):数据结构string、list
数据库·redis·缓存