【SQL】索引失效的11种情况

news2025/7/17 21:19:13

【SQL】索引失效的11种情况

  • 索引失效案例
    • 【1】. 全值匹配
    • 【2】. 最佳左前缀法则
    • 【3】. 主键插入顺序
    • 【4】. 计算、函数、类型转换(自动或手动)导致索引失效
    • 【5】. 类型转换导致索引失效
    • 【6】. 范围条件右边的列索引失效
    • 【7】. 不等于(!= 或者<>)索引失效
    • 【8】. is null可以使用索引,is not null无法使用索引
    • 【9】. like以通配符%开头索引失效
    • 【10】. OR 前后存在非索引的列,索引失效
    • 【11】. 数据库和表的字符集统一使用utf8mb4
    • 总结

数据库调优的大致方向:

  • 索引失效,没有充分利用到索引——建立索引
  • 关联查询太多join——sql优化
  • 服务器调优及各个参数设置——my.cnf
  • 数据过多——分库分表

sql查询优化技术有很多,大体分为物理查询优化逻辑查询优化:

  • 物理查询优化:通过索引和表连接方式等技术进行优化
  • 逻辑查询优化:通过SQL等价变换提升查询效率,就是换一种sql写法

数据准备:

CREATE DATABASE atguigudb2;
USE atguigudb2;

#############    class 表    #################
CREATE TABLE `class` (
`id` INT(11) NOT NULL AUTO_INCREMENT,
`className` VARCHAR(30) DEFAULT NULL,
`address` VARCHAR(40) DEFAULT NULL,
`monitor` INT NULL ,
PRIMARY KEY (`id`)
) ENGINE=INNODB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;

#############    student 表    #################
CREATE TABLE `student` (
`id` INT(11) NOT NULL AUTO_INCREMENT,
`stuno` INT NOT NULL ,
`name` VARCHAR(20) DEFAULT NULL,
`age` INT(3) DEFAULT NULL,
`classId` INT(11) DEFAULT NULL,
PRIMARY KEY (`id`)
#CONSTRAINT `fk_class_id` FOREIGN KEY (`classId`) REFERENCES `t_class` (`id`)
) ENGINE=INNODB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;

#################################

SET GLOBAL log_bin_trust_function_creators=1; # 不加global只是当前窗口有效。

#随机产生字符串
DELIMITER //
CREATE FUNCTION rand_string(n INT) RETURNS VARCHAR(255)
BEGIN
DECLARE chars_str VARCHAR(100) DEFAULT
'abcdefghijklmnopqrstuvwxyzABCDEFJHIJKLMNOPQRSTUVWXYZ';
DECLARE return_str VARCHAR(255) DEFAULT '';
DECLARE i INT DEFAULT 0;
WHILE i < n DO
SET return_str =CONCAT(return_str,SUBSTRING(chars_str,FLOOR(1+RAND()*52),1));
SET i = i + 1;
END WHILE;
RETURN return_str;
END //
DELIMITER ;
#假如要删除
#drop function rand_string;

#用于随机产生多少到多少的编号
DELIMITER //
CREATE FUNCTION rand_num (from_num INT ,to_num INT) RETURNS INT(11)
BEGIN
DECLARE i INT DEFAULT 0;
SET i = FLOOR(from_num +RAND()*(to_num - from_num+1)) ;
RETURN i;
END //
DELIMITER ;
#假如要删除
#drop function rand_num;

#创建往stu表中插入数据的存储过程
DELIMITER //
CREATE PROCEDURE insert_stu( START INT , max_num INT )
BEGIN
DECLARE i INT DEFAULT 0;
SET autocommit = 0; #设置手动提交事务
REPEAT #循环
SET i = i + 1; #赋值
INSERT INTO student (stuno, NAME ,age ,classId ) VALUES
((START+i),rand_string(6),rand_num(1,50),rand_num(1,1000));
UNTIL i = max_num
END REPEAT;
COMMIT; #提交事务
END //
DELIMITER ;

#执行存储过程,往class表添加随机数据
DELIMITER //
CREATE PROCEDURE `insert_class`( max_num INT )
BEGIN
DECLARE i INT DEFAULT 0;
SET autocommit = 0;
REPEAT
SET i = i + 1;
INSERT INTO class ( classname,address,monitor ) VALUES
(rand_string(8),rand_string(10),rand_num(1,100000));
UNTIL i = max_num
END REPEAT;
COMMIT;
END //
DELIMITER ;

#执行存储过程,往class表添加1万条数据
CALL insert_class(10000);

#执行存储过程,往stu表添加50万条数据
CALL insert_stu(100000,500000);

SELECT COUNT(*) FROM class;
SELECT COUNT(*) FROM student;

############################### 删除索引的存储过程 ########################
DELIMITER //
CREATE PROCEDURE `proc_drop_index`(dbname VARCHAR(200),tablename VARCHAR(200))
BEGIN
DECLARE done INT DEFAULT 0;
DECLARE ct INT DEFAULT 0;
DECLARE _index VARCHAR(200) DEFAULT '';
DECLARE _cur CURSOR FOR SELECT index_name FROM
information_schema.STATISTICS WHERE table_schema=dbname AND table_name=tablename AND
seq_in_index=1 AND index_name <>'PRIMARY' ;
#每个游标必须使用不同的declare continue handler for not found set done=1来控制游标的结束
DECLARE CONTINUE HANDLER FOR NOT FOUND SET done=2 ;
#若没有数据返回,程序继续,并将变量done设为2
OPEN _cur;
FETCH _cur INTO _index;
WHILE _index<>'' DO
SET @str = CONCAT("drop index " , _index , " on " , tablename );
PREPARE sql_str FROM @str ;
EXECUTE sql_str;
DEALLOCATE PREPARE sql_str;
SET _index='';
FETCH _cur INTO _index;
END WHILE;
CLOSE _cur;
END //
DELIMITER ;
# 执行存储过程
CALL proc_drop_index("dbname","tablename");

索引失效案例

【1】. 全值匹配

# 【1】. 全值匹配
# student表,主键id,此时无索引,耗时大
EXPLAIN SELECT SQL_NO_CACHE * FROM student WHERE age = 30;
EXPLAIN SELECT SQL_NO_CACHE * FROM student WHERE age = 30 AND classId = 4;
EXPLAIN SELECT SQL_NO_CACHE * FROM student WHERE age = 30 AND classId = 4 AND NAME = 'abcd';

# 注:SQL_NO_CACHE 不使用查询缓存

# 建立索引
CREATE INDEX idx_age ON student(age);
CREATE INDEX idx_age_classid ON student(age,classId);
CREATE INDEX idx_age_classid_name ON student(age,classId,NAME);	
# 此时第三条查询语句默认使用最后一条索引,而不是前两个

【2】. 最佳左前缀法则

# 【2】. 最佳左前缀法则
EXPLAIN SELECT SQL_NO_CACHE * FROM student WHERE student.age = 30 AND student.name = 'abcd';	
# 查age&name,用age的索引

EXPLAIN SELECT SQL_NO_CACHE * FROM student WHERE student.classid = 1 AND student.name = 'abcd';	
# 查classid&name,classid在前,有索引的话先找classid相同的,再找name,
#但现在没有这样的索引,idx_age_classid_name的字段顺序是先找age,所以不符合,所以此时不能用索引

EXPLAIN SELECT SQL_NO_CACHE * FROM student 
WHERE classid = 4 AND student.age = 30 AND student.name = 'abcd';	
#idx_age_classid_name 联合索引中所有字段均出现,可以使用该索引

EXPLAIN SELECT SQL_NO_CACHE * FROM student
WHERE student.age = 30 AND student.name = 'abcd';
# 现在,删除idx_age和idx_age_classid,发现用到idx_age_classid_name,而key_len=5,即只用到age字段,int(4)+null(1)
#因为索引完age后没有classid了,不能再查找到name

在这里插入图片描述

【3】. 主键插入顺序

在定义表时,让主键auto_increment,否则,插入一条数据时可能会移动大量数据。
如,往 1 5 8 10 15 … 100 中插9,会放在8 10 中间,因为索引默认升序排列。那么10往后的数据都要挪动,页不够时又要放到下一页,每插一条数据都这样挪一次,开销很大
我们自定义的主键列id 拥有AUTO_INCREMENT 属性,在插入记录时存储引擎会自动为我们填入自增的主键值。这样的主键占用空间小,顺序写入,减少页分裂。

【4】. 计算、函数、类型转换(自动或手动)导致索引失效

# 【4】. 计算、函数、类型转换(自动或手动)导致索引失效
##### 例1:
EXPLAIN SELECT SQL_NO_CACHE * FROM student WHERE student.name LIKE 'abc%';	#更好,能够使用上索引
# type=range 使用了索引中的排序

EXPLAIN SELECT SQL_NO_CACHE * FROM student WHERE LEFT(student.name,3) = 'abc';	# left(text,num_chars):截取左侧n个字符
# type = all 全表的访问
# 该语句的执行过程:针对每一条数据,一个一个取出,先作用一遍函数,再拿函数结果与abc对比,用不上b+树

CREATE INDEX idx_name ON student(NAME);

##### 例2:
CREATE INDEX idx_sno ON student(stuno);
EXPLAIN SELECT SQL_NO_CACHE id,stuno,NAME FROM student WHERE stuno+1 = 900001; 	# type = all 需要做运算,无法直接用索引找值

EXPLAIN SELECT SQL_NO_CACHE id,stuno,NAME FROM student WHERE stuno = 900000; 	# type = ref

【5】. 类型转换导致索引失效

# 【5】. 类型转换导致索引失效
# 未使用到索引
EXPLAIN SELECT SQL_NO_CACHE * FROM student WHERE NAME=123;	# 这里使用了隐式转换
# 使用到索引
EXPLAIN SELECT SQL_NO_CACHE * FROM student WHERE NAME='123'; 	# name本身就是字符串类型

【6】. 范围条件右边的列索引失效

# 【6】. 范围条件右边的列索引失效 ( > < >= <= between 等)
SHOW INDEX FROM student;
CALL proc_drop_index('atguigudb2','student');

CREATE INDEX idx_age_classid_name ON student(age,classId,NAME);

EXPLAIN SELECT SQL_NO_CACHE * FROM student
WHERE student.age = 30 AND student.classId > 20 AND student.name = 'abc';	# 这三个and先写谁无所谓,优化器会调优
# key_len = 10, age=5,classId=5,name用不上。classId 是范围,索引右侧的name用不上

# 改写索引:
CREATE INDEX idx_age_name_cid ON student(age,NAME,classId); 	#把需要排序的classid放到最后
# 此时在执行上面的语句,就使用了这个索引,key_len=73

创建的联合索引中,必须把涉及到范围的字段写在最后。

【7】. 不等于(!= 或者<>)索引失效

# 【7】. 不等于(!= 或者<>)索引失效
CREATE INDEX idx_name ON student(NAME);

EXPLAIN SELECT SQL_NO_CACHE * FROM student WHERE student.name <> 'abc';	# 索引失效 索引查的是等于

【8】. is null可以使用索引,is not null无法使用索引

# 【8】. is null可以使用索引,is not null无法使用索引
EXPLAIN SELECT SQL_NO_CACHE * FROM student WHERE age IS NULL;	# type=ref 相当于等于某个值
EXPLAIN SELECT SQL_NO_CACHE * FROM student WHERE age IS NOT NULL;	# 索引失效 相当于不等于

在这里插入图片描述

【9】. like以通配符%开头索引失效

# 【9】. like以通配符%开头索引失效
EXPLAIN SELECT SQL_NO_CACHE * FROM student WHERE NAME LIKE 'ab%';	# 可用索引

EXPLAIN SELECT SQL_NO_CACHE * FROM student WHERE NAME LIKE '%ab';	# type = all 索引失效

页面搜索严禁左模糊或者全模糊,如果需要请走搜索引擎来解决。

【10】. OR 前后存在非索引的列,索引失效

# 【10】. OR 前后存在非索引的列,索引失效 

CALL proc_drop_index('atguigudb2','student');
SHOW INDEX FROM student;
# 创建一个age的索引
CREATE INDEX idx_age ON student(age);

EXPLAIN SELECT SQL_NO_CACHE * FROM student WHERE age = 10 OR classid = 100;	# 未使用索引,索引+全表扫描->全表扫描
# 再加一个字段的单独索引
CREATE INDEX idx_cid ON student(classid);
# 再执行上条语句,此时 type = index_merge ,key = idx_age,idx_cid。

【11】. 数据库和表的字符集统一使用utf8mb4

统一使用utf8mb4( 5.5.3版本以上支持)兼容性更好,统一字符集可以避免由于字符集转换产生的乱码。不同的字符集进行比较前需要进行转换会造成索引失效。

总结

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/33017.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

如何在openstack环境下实现高性能的网络服务

大家晚上好。那我们开始吧。主要还是抛装引玉&#xff0c;互相学习交流。今天和大家分享下面一些内容&#xff1a; 1.关于openstack中VNF网络性能的一些思考和思路 2.相关的开源项目 3.OVS 2.4 DPDK with IVSHMEM/vHost-user(w/DPDK) 和vHost (w/oDPDK)性能测试数据 4.后续可以…

3.55 OrCAD中怎么批量修改属性值字体的大小?OrCAD的Occurrence属性与Instance属性是什么含义?

笔者电子信息专业硕士毕业&#xff0c;获得过多次电子设计大赛、大学生智能车、数学建模国奖&#xff0c;现就职于南京某半导体芯片公司&#xff0c;从事硬件研发&#xff0c;电路设计研究。对于学电子的小伙伴&#xff0c;深知入门的不易&#xff0c;特开次博客交流分享经验&a…

[附源码]java毕业设计疫情防控下高校教职工健康信息管理系统

项目运行 环境配置&#xff1a; Jdk1.8 Tomcat7.0 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术&#xff1a; SSM mybatis Maven Vue 等等组成&#xff0c;B/S模式 M…

TypeScript(五)知识扩展

TypeScript TypeScript模块化 JavaScript 有一个很长的处理模块化代码的历史&#xff0c;TypeScript 从 2012 年开始跟进&#xff0c;现在已经实现支持了很多格式。但是随着 时间流逝&#xff0c;社区和 JavaScript 规范已经使用为名为 ES Module的格式&#xff0c;这也就是我…

【Mysql】表的增删查改

文章目录前言一.表中插入数据1.1 全列增加1.2 指定列增加1.3 一次性插入多行数据1.4. 插入查询结果二.表的更新和替换2.1. 表的插入更新2.2. 替换三.表的查询3.1. 全列查询3.2. 指定列查询3.3. 查询字段为表达式并取别名3.4. 结果去重3.4. where条件3.5. 结果排序3.6. 分页查询…

【正点原子FPGA连载】第九章 按键控制LED实验摘自【正点原子】DFZU2EG/4EV MPSoC 之FPGA开发指南V1.0

1&#xff09;实验平台&#xff1a;正点原子MPSoC开发板 2&#xff09;平台购买地址&#xff1a;https://detail.tmall.com/item.htm?id692450874670 3&#xff09;全套实验源码手册视频下载地址&#xff1a; http://www.openedv.com/thread-340252-1-1.html 第九章 按键控制…

骨传导耳机是怎么传声的?骨传导耳机到底有哪些好处?

骨传导耳机最近在网上也是越来越火了&#xff0c;作为一种可以开放双耳的耳机&#xff0c;对比一般的耳机&#xff0c;它可以做到开放双耳&#xff0c;不堵耳道&#xff0c;保持耳朵健康卫生。 但是随着这种耳机的逐渐崛起&#xff0c;很多人也对这种耳机产生了疑惑&#xff1…

正版软件|WonderFox Photo Watermark 图片水印批量处理软件

Photo Watermark 可以为您做什么&#xff1f;水印是最有效的方法&#xff0c;可防止他人擅自使用您的照片。只需单击几下&#xff0c;Watermark Software 将为您的图像文件提供不可磨灭的保护。 可自定义的水印 文本水印 只需按键即可创建支持丰富字体&#xff0c;符号&#x…

Redis进阶(主从复制、Redis集群、缓存穿透、缓存击穿、缓存雪崩)

目录 1、主从复制&#xff08;读写分离&#xff09; 1.1、什么是主从复制 1.2、主从复制的作用 1.3、环境搭建 1.4、一主二仆 1.5、注意事项 1.6、反客为主 1.7、哨兵模式&#xff08;sentinel&#xff09; 2、Redis集群 2.1、什么是集群 2.2、什么是redis集群 2.3…

报表工具使用之FineReport大数据集导出插件

1. 概述 1.1 版本 报表服务器版本 插件版本 功能变更 11.0 V1.0.0 - 11.0.10 V1.1.16 1&#xff09;插件名称变更为&#xff1a;大数据集导出插件 2&#xff09;支持导出为 CSV 类型文件 1.2 应用场景 大批量数据导出的时候&#xff0c;会对服务器、网络传输、数据库…

Python eval()函数详解

一、eval()函数 用来执行一个字符串表达式&#xff0c;并返回表达式的值。可以把字符串转化为list,dict ,tuple。 二、eval()函数源码 三、eval()函数语法 eval(*args, **kwargs) 说明&#xff1a; expression -- 表达式。globals -- 变量作用域&#xff0c;全局命名空间&…

设计模式之美——单元测试和代码可测性

最可落地执行、最有效的保证重构不出错的手段应该就是单元测试&#xff08;Unit Testing&#xff09;。 什么是单元测试&#xff1f; 单元测试由研发工程师自己来编写&#xff0c;用来测试自己写的代码的正确性。我们常常将它跟集成测试放到一块来对比。单元测试相对于集成测…

【PyTorch】Torchvision Models

文章目录六、Torchvision Models1、VGG1.1 add1.2 modify2、Save and Load2.1 模型结构 模型参数2.2 模型参数&#xff08;官方推荐&#xff09;2.3 Trap六、Torchvision Models 1、VGG VGG参考文档&#xff1a;https://pytorch.org/vision/stable/models/vgg.html 以VGG16为…

apache-atlas-hbase-hook源码分析

元数据类型 Hbase元数据类型, 包括命令空间、表、列族、列 public enum HBaseDataTypes {// ClassesHBASE_NAMESPACE,HBASE_TABLE,HBASE_COLUMN_FAMILY,HBASE_COLUMN;public String getName() {return name().toLowerCase();} }Hbase元数据采集实现 1&#xff09;批量采集HBa…

【windows】实战部署二(使用)SVNserver服务端+SVNclient客户端

SVN服务器应用 创建版本库 1、打开VisualSVN Server&#xff1a; 2、建立版本库&#xff1a; 需要右键单击左边窗口的Repositores,在弹出的右键菜单中选择Create New Repository或者新建-Repository 3、默认选择&#xff0c;点击 “下一步” 按钮&#xff1a; Regular FSFS…

物联网安全年报漏洞情况

物联网 威胁分析漏洞篇物联网威胁分析—漏洞篇 引言 本章将从漏洞利用角度对物联网威胁进行分析。首先&#xff0c;我们分析了 NVD和 Exploit-DB中的物联网 年度漏洞及利用 1 变化趋势&#xff1b;之后统计了绿盟威胁捕获系统捕获到的物联网漏洞利用的整体情况&#xff1b;最…

Matlab深度学习实战一:LeNe-5图像分类篇MNIST数据集分十类且matlab提供模型框架全网为唯一详细操作流程

1.数据集简介下载与准备 2.matlab搭建模型相关知识 3.matlab软件的操作过程&#xff1a; &#xff08;1&#xff09;界面操作 &#xff08;2&#xff09;深度学习设计器使用 &#xff08;3&#xff09;图像数据导入 &#xff08;4&#xff09;训练可视化 一、数据集简介下载与…

mysql基本命令操作

MySQL数据库管理 SQL语句 SQL语句用于维护管理数据库&#xff0c;包括数据查询、数据更新、访问控制、对象管理等功能 DDL&#xff1a;数据定义语言&#xff0c;用于创建数据库对象&#xff0c;如库、表、索引等 DML&#xff1a;数据操纵语言&#xff0c;用于对表中的数据进行…

[附源码]计算机毕业设计JAVA民宿网站管理系统

[附源码]计算机毕业设计JAVA民宿网站管理系统 项目运行 环境配置&#xff1a; Jdk1.8 Tomcat7.0 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术&#xff1a; SSM mybati…

Android 10.0 11.0 12.0 启动模拟器教程

Android 10.0 11.0 12.0 启动模拟器教程 一、android 12.0 模拟器二、创建模拟器设备三、创建删除路经文件夹avd和配置环境变量四、启动模拟器一、android 12.0 模拟器 Android 10.0 11.0 12.0 启动模拟器都行,我选择android 12.0 模拟器 二、创建模拟器设备 第一步骤:在 …