25_mysql数据库优化测试数据准备与索引失效案例

本文涉及的产品
RDS MySQL Serverless 基础系列,0.5-2RCU 50GB
云数据库 RDS MySQL,集群系列 2核4GB
推荐场景:
搭建个人博客
云数据库 RDS MySQL,高可用系列 2核4GB
简介: 每天进步亿点点
参考来源:

康师傅:https://www.bilibili.com/video/BV1iq4y1u7vj?p=141

爱编程的大李子:https://blog.csdn.net/LXYDSF/article/details/126606855

一、优化概述

都有哪些纬度可以进行数据库调优?简言之:

  • 索引失效、没有充分利用所以——索引建立
  • 关联查询太多 JOIN(设计缺陷或不得已的需求)——SQL 优化
  • 服务器调优及各个参数设置(缓冲、 线程数)——调整 my.cnf
  • 数据过多——分库分表

关于数据库调优的知识点非常分散,不同 DBMS,不同的公司,不同的职位,不同的项目遇到的问题都不尽相同。

虽然 SQL 查询优化的技术很多,但是大体方向上完全可以分为 物理查询优化逻辑查询优化 两大块。

  • 物理查询优化是通过 索引表连接方式 等技术来进行优化,这里重点需要掌握索引的使用
  • 逻辑查询优化就是通过 SQL 等价变换 提升查询效率,直白一点来讲就是,换一种执行效率更高的查询写法

二、数据准备

学员表插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表中插入数据的存储过程
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表添加80万条数据 
CALL insert_stu(100000,800000);

查询下数据是否插入成功

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

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

三、索引失效案例

MySQL 中提高性能的一个最有效的方式是对数据表设计合理的索引。索引提供了高效访问数据的方法,并且加快查询的速度,因此索引对查询的速度有着至关重要的影响。

  • 使用索引可以 快速地定位 表中的某条记录,从而提高数据库查询的速度,提高数据库的性能。
  • 如果查询时没有使用索引,查询语句就会 扫描表中的所有记录。在数据量大的情况下,这样查询的速度会很慢。

大多数情况下都(默认)采用 B+树 来构建索引。只是空间列类型的索引使用 R-树,并且 MEMORY 表还支持 hash 索引

其实,用不用索引,最终都是优化器说了算。优化器是基于什么的优化器?基于 cost 开销(CostBaseOptimizer),它不是基于规则(Rule-BasedOptimizer),也不是基于语义。怎么样开销小就怎么来。另外,SQL 语句是否使用索引,跟数据库版本、数据量、数据选择度都有关系

这里讲的是基于 B+树的索引失效案例, 了解 B+树可以更好的了解索引失效原理

1. 全值匹配

全值匹配可以充分的利用 复合 (组合、联合) 索引,建立几个复合索引字段,WHERE 里面最好就用上几个字段。且按照顺序来用。否则会造成后面的索引失效

比如建立索引 index(a,b,c),如果 where 中只用了 a 和 c,那么只有 a 字段使用了索引,c 字段索引就失效了,因为 b 字段没用上。

2. 最左匹配原则

在 MySQL 建立联合索引时会遵守最佳左前缀匹配原则,即最左优先,在检索数据时从联合索引的最左边开始匹配。

同上面的 全值匹配 一样,查询时如果字段没有按照组合索引建立的顺序,优化器会执行优化,会调整查询条件的顺序。不过在开发过程中我们还是要保持良好的开发习惯。

结论:MySQL 可以为多个字段创建索引,一个索引可以包括 16 个字段,对于多列字段,过滤条件要使用索引那必须按照索引建立时的顺序,依次满足,一旦跳过某个字段,索引后面的字段都无法使用。如果查询条件中没有使用这些字段中的第一个字段时,多列索引不会被使用。

拓展:Alibaba《Java开发手册》

索引文件具有 B-Tree 的最左前缀匹配特性,如果左边的值未确定,那么无法使用此索引。

3. 计算、函数、类型转换导致索引失效

SQL 语句中中 WHERE 后面使用 计算、函数、类型转换(自动或手动)导致索引失效

# LEFT 函数导致索引失效
SELECT SQL_NO_CACHE * FROM student WHERE LEFT(student.name,3) = 'abc';

# 条件中有计算时致索引失效
SELECT SQL_NO_CACHE id, stuno, NAME FROM student WHERE stuno+1 = 900001; 

# 表中name字段为字符类型,但是条件写了 int 整形 name = 123 发生类型转换,相当于使用了隐形 函数,索引失效
SELECT SQL_NO_CACHE * FROM student WHERE name=123;

实际测试中 字段为 int 类型,而 WHERE 后面条件写成字符串类型 '123' 并不会导致索引失效

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

# 对于组合索引 index(age, classId, name),classId使用范围查找(<、<=、>、>= 和 between 等) 导致 name 字段索引失效
SELECT * FROM student WHERE student.age=30 AND student.classId>20 AND student.name = 'abc' ;
解释一下为什么范围查询会导致索引失效:

因为根据范围查找筛选后的数据,无法保证范围查找后面的字段是有序的。

例如:a_b_c这个索引,你根据b范围查找>2的,在满足b>2的情况下,如b:3,4,c可能是5,3、因为c无序,那么c的索引便失效了

改进:

可以把要范围查找的字段放在组合索引的最后一个,例如把上面的 index(age, classId, name) 改成 index(age, name, classId)

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

使用等于精确查找可以使用索引,但是使用不等于之后,筛选出来的数据太多,还不如直接全表扫描,少了索引查找和回表的过程。

6. IS NOT NULL、NOT LIKE 无法使用索引

原理同上,IS NOT NULL、NOT LIKE 搜索的数据量太多,索引失效

7. LIKE 以通配符 % 开头索引失效

在使用 LIKE 关键字进行查询的查询语句中,不确定开头是哪些字符,无法精确匹配,必须全表扫描

拓展:Alibaba《Java 开发手册》

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

8. OR 前后存在非索引的列,索引失效

OR 的含义就是两个只要满足一个即可,因此只要有条件列没有进行索引,就会进行全表扫描,因此索引的条件列也会失效。因为 OR前后一个使用索引,一个进行全表扫描,还没有直接进行全表扫描更快。

9. 数据库和表的字符集统一使用 utf8mb4/utf8mb3

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

10. JOIN 两个表的字段不一致,索引失效

类似于第三点,存在类型转换,导致索引失效

建议:

  • 对于单列索引,尽量选择针对当前 query 过滤性更好的索引
  • 在选择组合索引的时候,当前 query 中过滤性最好的字段在索引字段顺序中,位置越靠前越好
  • 在选择组合索引的时候,尽量选择能够包含当前 query 中的 where 子句中更多字段的索引
  • 在选择组合索引的时候,如果某个字段可能出现范围查询时,尽量把这个字段放在索引次序的最后面。

总之,书写 SQL 语句时,尽量避免造成索引失效的情况。

相关实践学习
如何快速连接云数据库RDS MySQL
本场景介绍如何通过阿里云数据管理服务DMS快速连接云数据库RDS MySQL,然后进行数据表的CRUD操作。
全面了解阿里云能为你做什么
阿里云在全球各地部署高效节能的绿色数据中心,利用清洁计算为万物互联的新世界提供源源不断的能源动力,目前开服的区域包括中国(华北、华东、华南、香港)、新加坡、美国(美东、美西)、欧洲、中东、澳大利亚、日本。目前阿里云的产品涵盖弹性计算、数据库、存储与CDN、分析与搜索、云通信、网络、管理与监控、应用服务、互联网中间件、移动服务、视频服务等。通过本课程,来了解阿里云能够为你的业务带来哪些帮助 &nbsp; &nbsp; 相关的阿里云产品:云服务器ECS 云服务器 ECS(Elastic Compute Service)是一种弹性可伸缩的计算服务,助您降低 IT 成本,提升运维效率,使您更专注于核心业务创新。产品详情: https://www.aliyun.com/product/ecs
目录
相关文章
|
1天前
|
缓存 NoSQL JavaScript
Vue.js应用结合Redis数据库:实践与优化
将Vue.js应用与Redis结合,可以实现高效的数据管理和快速响应的用户体验。通过合理的实践步骤和优化策略,可以充分发挥两者的优势,提高应用的性能和可靠性。希望本文能为您在实际开发中提供有价值的参考。
21 11
|
11天前
|
数据库 Windows
SqlServer数据恢复—SqlServer数据库所在分区损坏的数据恢复案例
一块硬盘上存放的SqlServer数据库,windows server操作系统+NTFS文件系统。由于误操作导致分区损坏,需要恢复硬盘里的SqlServer数据库数据。
|
1天前
|
缓存 关系型数据库 MySQL
【深入了解MySQL】优化查询性能与数据库设计的深度总结
本文详细介绍了MySQL查询优化和数据库设计技巧,涵盖基础优化、高级技巧及性能监控。
|
1月前
|
SQL Oracle 数据库
使用访问指导(SQL Access Advisor)优化数据库业务负载
本文介绍了Oracle的SQL访问指导(SQL Access Advisor)的应用场景及其使用方法。访问指导通过分析给定的工作负载,提供索引、物化视图和分区等方面的优化建议,帮助DBA提升数据库性能。具体步骤包括创建访问指导任务、创建工作负载、连接工作负载至访问指导、设置任务参数、运行访问指导、查看和应用优化建议。访问指导不仅针对单条SQL语句,还能综合考虑多条SQL语句的优化效果,为DBA提供全面的决策支持。
69 11
|
1月前
|
存储 关系型数据库 MySQL
mysql怎么查询longblob类型数据的大小
通过本文的介绍,希望您能深入理解如何查询MySQL中 `LONG BLOB`类型数据的大小,并结合优化技术提升查询性能,以满足实际业务需求。
127 6
|
1月前
|
关系型数据库 MySQL 数据库
数据库数据恢复—MYSQL数据库文件损坏的数据恢复案例
mysql数据库文件ibdata1、MYI、MYD损坏。 故障表现:1、数据库无法进行查询等操作;2、使用mysqlcheck和myisamchk无法修复数据库。
|
1月前
|
SQL 关系型数据库 MySQL
mysql分页读取数据重复问题
在服务端开发中,与MySQL数据库进行数据交互时,常因数据量大、网络延迟等因素需分页读取数据。文章介绍了使用`limit`和`offset`参数实现分页的方法,并针对分页过程中可能出现的数据重复问题进行了详细分析,提出了利用时间戳或确保排序规则绝对性等解决方案。
|
2月前
|
SQL 存储 BI
gbase 8a 数据库 SQL合并类优化——不同数据统计周期合并为一条SQL语句
gbase 8a 数据库 SQL合并类优化——不同数据统计周期合并为一条SQL语句
|
1月前
|
存储 Oracle 关系型数据库
服务器数据恢复—华为S5300存储Oracle数据库恢复案例
服务器存储数据恢复环境: 华为S5300存储中有12块FC硬盘,其中11块硬盘作为数据盘组建了一组RAID5阵列,剩下的1块硬盘作为热备盘使用。基于RAID的LUN分配给linux操作系统使用,存放的数据主要是Oracle数据库。 服务器存储故障: RAID5阵列中1块硬盘出现故障离线,热备盘自动激活开始同步数据,在同步数据的过程中又一块硬盘离线,RAID5阵列瘫痪,上层LUN无法使用。
|
28天前
|
存储 Oracle 关系型数据库
数据库传奇:MySQL创世之父的两千金My、Maria
《数据库传奇:MySQL创世之父的两千金My、Maria》介绍了MySQL的发展历程及其分支MariaDB。MySQL由Michael Widenius等人于1994年创建,现归Oracle所有,广泛应用于阿里巴巴、腾讯等企业。2009年,Widenius因担心Oracle收购影响MySQL的开源性,创建了MariaDB,提供额外功能和改进。维基百科、Google等已逐步替换为MariaDB,以确保更好的性能和社区支持。掌握MariaDB作为备用方案,对未来发展至关重要。
56 3

热门文章

最新文章