Mysql进阶优化篇02——索引失效的10种情况及原理(一)

本文涉及的产品
RDS MySQL Serverless 基础系列,0.5-2RCU 50GB
云数据库 RDS MySQL,集群系列 2核4GB
推荐场景:
搭建个人博客
RDS MySQL Serverless 高可用系列,价值2615元额度,1个月
简介: 文章目录1️⃣ 数据库调优的场景2️⃣数据准备

文章目录

1️⃣ 数据库调优的场景

2️⃣数据准备

(1)建表

(2)设置参数

(3)创建函数

(4)创建存储过程

(5)调用存储过程

(6)删除某表上的索引

3️⃣索引失效的情况

3.1 全值匹配我最爱(索引最佳)

3.2 不遵守最左前缀匹配原则

3.3 不按照递增顺序插入主键

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

3.5 类型转换导致索引失效

3.6 范围条件右边的列索引失效

3.7 不等于(!= 或者 <>)索引失效

3.8 is null可以使用索引,is not null无法使用索引

3.9 like 以通配符 % 开头索引失效

3.10 OR前后存在非索引的列

3.11 数据库和表的字符集不匹配

4.索引一般性建议

1️⃣ 数据库调优的场景

上一篇mysql进阶优化篇,我们介绍了数据库的性能分析工具,知道了怎么发现数据库的性能问题,这一篇博客我们将介绍索引失效的10种情况及原理


先来了解下可能需要进行数据库优化的场景。


索引失效(按准则编写、调整sql)

没有充分利用到索引(建立索引)

关联查询太多的JOIN(JOIN查询性能与表的数目成指数相关,一般不超过三张,否则需要进行sql优化或者进行反范式化设计,增加必要的冗余)

服务器调优与各个参数的设置,如缓存、线程数等(修改my.conf)

数据过高(已经在软件层面充分调优,但仍不能面对高并发场景,可以考虑分表分库分散服务器压力)

接下来我们介绍下sql查询优化。虽然sql查询优化技术很多,但是大致不离 物理查查询优化 和 逻辑查询优化 两大块。


物理查询优化:通过索引和表连接方式进行优化

逻辑查询优化:通过sql语句的等价代换,实现数据库查询的优化。

2️⃣数据准备

学员表插50万 条, 班级表插1万条。


(1)建表

#班级表
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;
#学员表
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;

(2)设置参数

命令开启:允许创建函数设置

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

(3)创建函数

保证每条数据都不同

#随机产生字符串
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;

(4)创建存储过程

创建往stu表中插入数据的存储过程

#创建往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 ;
#假如要删除
#drop PROCEDURE insert_stu;

创建往class表中插入数据的存储过程

#执行存储过程,往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 ;
#假如要删除
#drop PROCEDURE insert_class;

5)调用存储过程

往class表添加1万条数据

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

往stu表添加50万条数据,这个时间会稍微有点长,请耐心等待哟。

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

(6)删除某表上的索引

创建删除索引存储过程。这是为了方便我们的学习,因为我们在演示某个索引的效果时,可能需要删除其它索引,如果需要一个个手工删除,就太费劲了

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

3️⃣索引失效的情况

这里我们以InnoDB的B+树的索引结构作为讲解的重点,讲解索引失效的案例(3.1讲解索引最佳的实践)。之所以会出现索引失效的情况,其实是因为我们的优化器经过了成本开销的计算,决定不用索引。用不用索引都是优化器说了算,Sql语句是否会使用索引,跟数据库版本、数据量和数据选择度都有关系。


3.1 全值匹配我最爱(索引最佳)

全值匹配可以充分的利用组合索引。


在没有建立索引时会进行数据查询速度会比较慢

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';

QL_NO_CACHE表示不使用查询缓存。

下图是在没有创建索引的情况下,第一条sql的执行效果。其查询时间是0.048s。

下面建立下索引

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);

再次执行查询sql,就可以使用到索引idx_age。并且其查询耗时会变短为0.024s。


执行如下sql。选择的索引则是:idx_age_classid。思考下为什么?


EXPLAIN SELECT SQL_NO_CACHE * FROM student WHERE age=30 AND classId=4;

这是因为我们在构建索引idx_age_classid的B+树时,会先按照age排序,在按照calssId排序,对于这个sql来说,更加高效。


但是上面的索引可能不生效哦,在数据量较大的情况下,我们进行全值匹配SELECT *,优化器可能经过计算发现,我们使用索引查询所有的数据后,还需要对查找到的数据进行回表操作,性能还不如全表扫描。这里我们没有造这么多数据,所以就不演示效果咯。

相关实践学习
如何快速连接云数据库RDS MySQL
本场景介绍如何通过阿里云数据管理服务DMS快速连接云数据库RDS MySQL,然后进行数据表的CRUD操作。
全面了解阿里云能为你做什么
阿里云在全球各地部署高效节能的绿色数据中心,利用清洁计算为万物互联的新世界提供源源不断的能源动力,目前开服的区域包括中国(华北、华东、华南、香港)、新加坡、美国(美东、美西)、欧洲、中东、澳大利亚、日本。目前阿里云的产品涵盖弹性计算、数据库、存储与CDN、分析与搜索、云通信、网络、管理与监控、应用服务、互联网中间件、移动服务、视频服务等。通过本课程,来了解阿里云能够为你的业务带来哪些帮助 &nbsp; &nbsp; 相关的阿里云产品:云服务器ECS 云服务器 ECS(Elastic Compute Service)是一种弹性可伸缩的计算服务,助您降低 IT 成本,提升运维效率,使您更专注于核心业务创新。产品详情: https://www.aliyun.com/product/ecs
目录
打赏
0
0
0
0
4
分享
相关文章
Mysql的索引
MYSQL索引主要有 : 单列索引 , 组合索引和空间索引 , 用的比较多的就是单列索引和组合索引 , 空间索引我这边没有用到过 单列索引 : 在MYSQL数据库表的某一列上面创建的索引叫单列索引 , 单列索引又分为 ● 普通索引:MySQL中基本索引类型,没有什么限制,允许在定义索引的列中插入重复值和空值,纯粹为了查询数据更快一点。 ● 唯一索引:索引列中的值必须是唯一的,但是允许为空值 ● 主键索引:是一种特殊的唯一索引,不允许有空值 ● 全文索引: 只有在MyISAM引擎、InnoDB(5.6以后)上才能使⽤用,而且只能在CHAR,VARCHAR,TEXT类型字段上使⽤用全⽂文索引。
MySQL实现文档全文搜索,分词匹配多段落重排展示,知识库搜索原理分享
本文介绍了在文档管理系统中实现高效全文搜索的方案。为解决原有ES搜索引擎私有化部署复杂、运维成本高的问题,我们转而使用MySQL实现搜索功能。通过对用户输入预处理、数据库模糊匹配、结果分段与关键字标红等步骤,实现了精准且高效的搜索效果。目前方案适用于中小企业,未来将根据需求优化并可能重新引入专业搜索引擎以提升性能。
MySQL索引有哪些类型?
● 普通索引:最基本的索引,没有任何限制。 ● 唯一索引:索引列的值必须唯一,但可以有空值。可以创建组合索引,则列值的组合必须唯一。 ● 主键索引:是特殊的唯一索引,不可以有空值,且表中只存在一个该值。 ● 组合索引:多列值组成一个索引,用于组合搜索,效率高于索引合并。 ● 全文索引:对文本的内容进行分词,进行搜索。
RDS用多了,你还知道MySQL主从复制底层原理和实现方案吗?
随着数据量增长和业务扩展,单个数据库难以满足需求,需调整为集群模式以实现负载均衡和读写分离。MySQL主从复制是常见的高可用架构,通过binlog日志同步数据,确保主从数据一致性。本文详细介绍MySQL主从复制原理及配置步骤,包括一主二从集群的搭建过程,帮助读者实现稳定可靠的数据库高可用架构。
107 9
RDS用多了,你还知道MySQL主从复制底层原理和实现方案吗?
基于SQL Server / MySQL进行百万条数据过滤优化方案
对百万级别数据进行高效过滤查询,需要综合使用索引、查询优化、表分区、统计信息和视图等技术手段。通过合理的数据库设计和查询优化,可以显著提升查询性能,确保系统的高效稳定运行。
50 9
MySQL和SQLSugar百万条数据查询分页优化
在面对百万条数据的查询时,优化MySQL和SQLSugar的分页性能是非常重要的。通过合理使用索引、调整查询语句、使用缓存以及采用高效的分页策略,可以显著提高查询效率。本文介绍的技巧和方法,可以为开发人员在数据处理和查询优化中提供有效的指导,提升系统的性能和用户体验。掌握这些技巧后,您可以在处理海量数据时更加游刃有余。
108 9
图解MySQL【日志】——磁盘 I/O 次数过高时优化的办法
当 MySQL 磁盘 I/O 次数过高时,可通过调整参数优化。控制刷盘时机以降低频率:组提交参数 `binlog_group_commit_sync_delay` 和 `binlog_group_commit_sync_no_delay_count` 调整等待时间和事务数量;`sync_binlog=N` 设置 write 和 fsync 频率,`innodb_flush_log_at_trx_commit=2` 使提交时只写入 Redo Log 文件,由 OS 择机持久化,但两者在 OS 崩溃时有丢失数据风险。
50 3
【YashanDB知识库】原生mysql驱动配置连接崖山数据库
【YashanDB知识库】原生mysql驱动配置连接崖山数据库
【YashanDB知识库】原生mysql驱动配置连接崖山数据库
docker拉取MySQL后数据库连接失败解决方案
通过以上方法,可以解决Docker中拉取MySQL镜像后数据库连接失败的常见问题。关键步骤包括确保容器正确启动、配置正确的环境变量、合理设置网络和权限,以及检查主机防火墙设置等。通过逐步排查,可以快速定位并解决连接问题,确保MySQL服务的正常使用。
120 82