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

简介: 每天进步亿点点
参考来源:

康师傅: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 语句时,尽量避免造成索引失效的情况。

相关实践学习
每个IT人都想学的“Web应用上云经典架构”实战
本实验从Web应用上云这个最基本的、最普遍的需求出发,帮助IT从业者们通过“阿里云Web应用上云解决方案”,了解一个企业级Web应用上云的常见架构,了解如何构建一个高可用、可扩展的企业级应用架构。
MySQL数据库入门学习
本课程通过最流行的开源数据库MySQL带你了解数据库的世界。 &nbsp; 相关的阿里云产品:云数据库RDS MySQL 版 阿里云关系型数据库RDS(Relational Database Service)是一种稳定可靠、可弹性伸缩的在线数据库服务,提供容灾、备份、恢复、迁移等方面的全套解决方案,彻底解决数据库运维的烦恼。 了解产品详情:&nbsp;https://www.aliyun.com/product/rds/mysql&nbsp;
目录
相关文章
|
12月前
|
存储 人工智能 NoSQL
AI大模型应用实践 八:如何通过RAG数据库实现大模型的私有化定制与优化
RAG技术通过融合外部知识库与大模型,实现知识动态更新与私有化定制,解决大模型知识固化、幻觉及数据安全难题。本文详解RAG原理、数据库选型(向量库、图库、知识图谱、混合架构)及应用场景,助力企业高效构建安全、可解释的智能系统。
|
SQL 存储 监控
SQL日志优化策略:提升数据库日志记录效率
通过以上方法结合起来运行调整方案, 可以显著地提升SQL环境下面向各种搜索引擎服务平台所需要满足标准条件下之数据库登记作业流程综合表现; 同时还能确保系统稳健运行并满越用户体验预期目标.
535 6
|
存储 数据管理 数据库
数据字典是什么?和数据库、数据仓库有什么关系?
在数据处理中,你是否常困惑于字段含义、指标计算或数据来源?数据字典正是解答这些问题的关键工具,它清晰定义数据的名称、类型、来源、计算方式等,服务于开发者、分析师和数据管理者。本文详解数据字典的定义、组成及其与数据库、数据仓库的关系,助你夯实数据基础。
数据字典是什么?和数据库、数据仓库有什么关系?
|
数据采集 关系型数据库 MySQL
python爬取数据存入数据库
Python爬虫结合Scrapy与SQLAlchemy,实现高效数据采集并存入MySQL/PostgreSQL/SQLite。通过ORM映射、连接池优化与批量提交,支持百万级数据高速写入,具备良好的可扩展性与稳定性。
|
人工智能 Java 关系型数据库
使用数据连接池进行数据库操作
使用数据连接池进行数据库操作
294 11
|
SQL 关系型数据库 MySQL
Mysql数据恢复—Mysql数据库delete删除后数据恢复案例
本地服务器,操作系统为windows server。服务器上部署mysql单实例,innodb引擎,独立表空间。未进行数据库备份,未开启binlog。 人为误操作使用Delete命令删除数据时未添加where子句,导致全表数据被删除。删除后未对该表进行任何操作。需要恢复误删除的数据。 在本案例中的mysql数据库未进行备份,也未开启binlog日志,无法直接还原数据库。
1003 2
|
缓存 Java 应用服务中间件
Spring Boot配置优化:Tomcat+数据库+缓存+日志,全场景教程
本文详解Spring Boot十大核心配置优化技巧,涵盖Tomcat连接池、数据库连接池、Jackson时区、日志管理、缓存策略、异步线程池等关键配置,结合代码示例与通俗解释,助你轻松掌握高并发场景下的性能调优方法,适用于实际项目落地。
2009 5
|
数据可视化 前端开发 测试技术
接口测试新选择:Postman替代方案全解析
在软件开发中,接口测试工具至关重要。Postman长期占据主导地位,但随着国产工具的崛起,越来越多开发者转向更适合中国市场的替代方案——Apifox。它不仅支持中英文切换、完全免费不限人数,还具备强大的可视化操作、自动生成文档和API调试功能,极大简化了开发流程。
|
Java 测试技术 容器
Jmeter工具使用:HTTP接口性能测试实战
希望这篇文章能够帮助你初步理解如何使用JMeter进行HTTP接口性能测试,有兴趣的话,你可以研究更多关于JMeter的内容。记住,只有理解并掌握了这些工具,你才能充分利用它们发挥其应有的价值。+
1766 23

推荐镜像

更多