Mysql原理与调优-如何进行sql优化

news2025/1/11 14:04:32

1.绪论

本文主要讲解我们如何优化一个sql。优化的过程主要分为3个步骤,找到哪些sql需要被优化,这就需要用到慢sql日志。然后发现慢SQL为什么慢,即当前sql是如何执行的,这就需要用到执行计划。最后才是对sql进行优化,对于开发而言,一般是不会去调节Mysql服务的各种参数的,所以一般是从索引的角度对sql进行优化。

2.哪些sql需要优化-慢sql日志

2.1 什么是慢sql日志

当执行时间超过某个阈值或者某些sql未走索引,会被加入到慢sql日志中。

2.2 慢sql的相关参数

参数说明备注
slow_query_log是否开启慢sql日志0-不开启,1开启默认不开启,开启可能会影响性能
slow_query_log_file慢日志文件位置
long_query_time超过多少秒算慢sql一般设置为1秒
log_queries_not_using_indexes未走索引的sql是否会加入到慢日志文件中

3.慢sql现在是如何执行的-执行计划

3.1 数据准备

我们准备两张表分别是用户基础信息表tb_user和用户表tb_user_info。其中tb_user表示用户创建注册过系统便会在改变留下一条记录。而tb_user_info表示是用户对系统的使用信息。

CREATE TABLE `tb_user` (
  `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键',
  `phone` varchar(11) NOT NULL COMMENT '手机号码',
  `password` varchar(128) DEFAULT '' COMMENT '密码,加密存储',
  `nick_name` varchar(32) DEFAULT '' COMMENT '昵称,默认是用户id',
  `icon` varchar(255) DEFAULT '' COMMENT '人物头像',
  `create_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
  `update_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
  PRIMARY KEY (`id`) USING BTREE,
  UNIQUE KEY `uniqe_key_phone` (`phone`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=1010 DEFAULT CHARSET=utf8mb4 ROW_FORMAT=COMPACT
CREATE TABLE `tb_user_info` (
  `user_id` bigint(20) unsigned NOT NULL COMMENT '主键,用户id',
  `city` varchar(64) DEFAULT '' COMMENT '城市名称',
  `introduce` varchar(128) DEFAULT NULL COMMENT '个人介绍,不要超过128个字符',
  `fans` int(8) unsigned DEFAULT '0' COMMENT '粉丝数量',
  `followee` int(8) unsigned DEFAULT '0' COMMENT '关注的人的数量',
  `gender` tinyint(1) unsigned DEFAULT '0' COMMENT '性别,0:男,1:女',
  `birthday` date DEFAULT NULL COMMENT '生日',
  `credits` int(8) unsigned DEFAULT '0' COMMENT '积分',
  `level` tinyint(1) unsigned DEFAULT '0' COMMENT '会员级别,0~9级,0代表未开通会员',
  `create_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
  `update_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
  PRIMARY KEY (`user_id`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 ROW_FORMAT=COMPACT


3.2 什么是执行计划

Mysql的优化器在根据各种成本计算过后,会为该条sql生产一个执行计划。这个执行计划包含了表的驱动关系,最终执行的索引,大概得扫描行等。根据,执行计划,我们可以知道执行器最终是如何执行sql的,选择的索引是什么或者索引失效的原因是什么。我们可以通过explain关键字查询执行计划。

explain sql语句

3.3 explain查询结果详解

EXPLAIN SELECT * FROM tb_user

通过执行explain,我们可以得到如下信息,接下来我们就对每个字段进行解释。

3.3.1 id

sql语句每个select关键字就会生成一个id,针对id主要讲3种情况。

1.连接查询

连接查询id应该是一样的,但是上面的为驱动表,下面的为被驱动表。

explain SELECT * FROM tb_user u left join tb_user_info ui on u.id = ui.user_id

2. 子查询

子查询有多个select所以有多个id,id越大,越先执行。

EXPLAIN
SELECT * FROM tb_user WHERE id IN (
SELECT id WHERE nick_name='tom' 
) 

3. union

union会在临时表中去重,相当于再次执行了selct操作,但是在临时表中去重这一操作的id为null。

EXPLAIN
SELECT id FROM tb_user WHERE nick_name='tom' 
UNION
SELECT id FROM tb_user WHERE nick_name='jack'

3.3.2 select_type

select _type其实就是对于每个select的定义的属性,表示该select操作具体是一个什么类型的操作,一般和id是一一对应的。

1.simple

只要不包含子查询,union,union all便是simple。

explain select * from tb_user

2.union 和union all

union查询第一个查询类型为priamary,后面的查询类型为union,如果基于他们的结果在临时表中去重类型为union result。

3.子查询

子查询的外层查询为primary,内层查询为subquery

EXPLAIN
SELECT * FROM tb_user WHERE id IN (
SELECT id WHERE nick_name='tom' 
) 
4. 物化表

Mysql会把一些sql的查询结果,持久化到磁盘上,形成物化表。对于物化表的type为DERIVED。

EXPLAIN
SELECT * FROM  (
SELECT id,COUNT(id) AS num FROM tb_user GROUP BY id
) tmp

3.3.3 partitions

分区,mysql可以将数据持久化文件分别存储到不同的磁盘文件上,提升磁盘IO效率。partitions展示的就是分区信息,一般为空。

3.3.4 type

type表示单表是如何访问的。类型有:

systemconsteq_refreffulltextref_or_nullindex_mergeunique_subqueryindex_subqueryrangeindexALL

我们只需要了解常用的几个类型,并且类型性能排序如下:

system> const>ref> ranget>index>all

1. system

表中只有一条数据时,为system。

2.const

唯一索引并且等值访问。(注意,唯一索引里面可能为null,此时用is null会退化为ref)。

3.eq_ref

连接查询,被驱动表走唯一索引。

4. ref

走二级索引访问。

5. ref_or_null

如果走二级索引并且字段可能为null,可能为ref_or_null。

5.index_merge

index_merge主要包含Intersection、Union合并和Sort-Union合并。

一般出现这种的sql语句:

1) 什么是索引合并

主要是where条件有多个条件,条件之间or或者and连接,条件针对的是不同的列并且每个列都有索引。如果是and连接可能会出现Intersection,如果用or连接,可能出现union或者Sort-Union。

2) Intersection
SELECT * FROM tb_user WHERE phone = '13688668922' 
AND nick_name='user_p3655ctliy'

Intersection的执行步骤:

1.如果不采用索引合并,sql该怎么执行呢?上面sql语句可能会先走idx_phone这个索引,获取到phone=13688668922的主键id,然后到主键索引回表,过滤得到nick_name='user_p3655ctliy'的数据。

2.如果采用索引合并,应该怎么执行呢?其实就是取出的走idx_phone这个索引,获取到phone=13688668922的主键id的,然后走idx_nick_name获取到nick_name='user_p3655ctliy'的主键id,取交集,然后根据主键id交集到主键索引中回表,得到数据。

要执行Intersection满足哪些条件:

1.如果走的是非主键索引(可能是联合索引),必须是等值查询,并且索引中的每列都出现在where条件中。这是因为,如果存在某列不在的where条件中,查询出来的主键id可能是一个范围,与另一个索引得到的主键id取交集时,可能性能很低。

2.如果是主键索引,可以为范围查询。原因是,在intersection的时候,可以走二级索引获取到主键id,然后回表时根据主键id范围过滤即可。

3)Union合并

如果多个where条件采用or进行连接,可能会出现or的情况。union查询步骤和intersection的查询步骤是一样的,只是union查询是查出每棵索引树的id,然后取并集,最后回表。

4)索引合并的优化

索引可以通过给where条件后面的列建立联合索引进行优化。

6.unique_subquery

子查询走的是唯一索引进行连接。

7.index_subquery

子查询走非唯一索引进行连接。

8.range

如果有范围查询,或者in子句走索引,为range。

9.index

没有走索引,但是索引上面的列包含了where条件中的列,可以通过索引来进行过滤。

ALTER TABLE tb_user ADD INDEX idx_phone_create(`nick_name`,`create_time`);
EXPLAIN SELECT * FROM tb_user WHERE create_time = '2022-02-28 10:50:47'
10.All

全表扫描。

从上面可以看出,如果索引为index或者All,这个时候就已经没有走索引了,我们就需要考虑如果优化。

3.3.5 passible key和key

优化器会根据passible key里面的索引和全表扫描进行成本比较,最后会选择成本最低的key做为执行路径。

我们如果想查看优化器是如何锁定key最后最后的计划的,可以打开optimizer_trace:

SHOW VARIABLES LIKE 'optimizer_trace' //查看optimzer_trace是否打开

SET optimizer_trace="enabled=on"; //打开optimizer_trace

SELECT * FROM tb_user WHERE phone = '13688668922' AND nick_name='user_p3655ctliy';
    
SELECT * FROM information_schema.OPTIMIZER_TRACE; //查看优化过程

3.3.6 key_len

key_len表示的是索引字段的字节数,可以用它来判断联合索引被使用了几列:

1.如果是定长字段,key_len就是改字段占用的字节数;

2.如果是变成字段,为该字段所能包含的最大字节数,比如varchar(10)且采用utf8编码,则为key_len为30;同时,会多两个字节来存储变成字段的长度。

3.如果索引可以为null,比部位null多一个字节。

3.3.7ref

如果作等值匹配时,并且走索引,ref表示的是,做等值匹配的是什么内容。比如const就是一个常数。

3.3.8 rows

rows代表该执行计划最后需要扫描的行数。如果,扫描函数过大,但是又走了索引,此时就需要考虑花在回表上的成本。

3.3.9 filter

filter表示走当前索引后,满足最终条件的行数,占通过当前索引过滤出来的行数的百分比。我们举个例子。

explain SELECT * FROM tb_user WHERE id > 80 AND nick_name='user_p3655ctliy'

可以看出,前面SELECT * FROM tb_user WHERE id > 80 会走主键索引过滤,得到大概扫描行数929行,然后再在这929行中,进行过滤出nick_name='user_p3655ctliy'的记录,优化器估这里又92.9行满足条件。

3.3.10 extra

extra是explain中很关键的一个字段,里面包含了这个sql具体使用的优化技术。

1.useing index

表示索引覆盖,不需要回表。

2. using index condition

表示使用索引下推。

EXPLAIN SELECT * FROM tb_user WHERE nick_name LIKE 'j%' 
AND nick_name LIKE '%a%'

如果未使用索引下推之前,这种多个条件不能形成范围的条件,怎么执行呢?

1.通过二级索引查询出以j开头的记录的主键id;

2.通过主键id到主键索引回表,查询出完整记录返回给server层;

3.server层根据主键索引过滤出nick_name包含a的记录。

​​​​​​如果使用索引下推:

1.通过二级索引查询出以j开头的记录;

2.由于该二级索引包含另一个条件的字段,所以可以直接在索引里面进行判断,得到包含的字段a的记录的主键id;

3.到主键索引,进行回表返回完整记录。

3. using where

表示先走索引,再回表后将记录返回给server层,server对剩余条件进行过滤。

4.using join buffer

表示连接查询的时候,会采用join buffer来进行优化。在连接查询的时候,其实就类似于二重for循环,针对这个二重for循环,Mysql页提出了很多优化手段。我们看看下面这条语句是如何工作的:
 

SELECT * FROM tb_user u LEFT JOIN tb_user_info ui ON u.id = ui.user_id
1) Simple Nested-Loop Join( 简单的嵌套循环连接 )

其实就是先根据where条件过滤出tb_user中的数据,然后的取出连接值,依次到的tb_user_info中去查询。

2) Index Nested-Loop Join( 索引嵌套循环连接 )

索引嵌套查询其实就是驱动表取出连接值,然后到被驱动表的索引进行匹配。

3) lock Nested-Loop Join( 块嵌套循环连接 ) 

块嵌套查询就是依次从驱动表中获取的多条数据,到join buffer中(这样可以减少驱动表的磁盘IO操作),然后一次性到被驱动表中进行匹配。

4) 连接查询优化

1.小表驱动大表,原因主要是驱动表在走where条件后,再过滤的数据全部扫描一遍,而被驱动表可以走索引,所以被驱动表越小越好;join buffer大小是有限的也可以作为另一个原因。

2.尽量给被驱动表的连接字段建立索引。

5.using filesort

在order by,distinct,group by的时候,会默认进行排序。如果不能走索引的话,会将数据加载到sort buffer中,利用排序算法进行排序。这个过程是比较耗时的。因为索引是天然有序的,所以我们尽量利用索引进行排序。

注意:Group by也会带着排序。

如果要禁用掉分组的排序,我们可以在后面加上order by null。

explain select nick_name ,count(id) as num from tb_user 
Group by nick_name order by null
6.Using intersect ,using union

表示采用了索引合并

7.Using temporary

表示采用了零时表,如果执行了union,distict,group by等操作的时候,可能建立内部临时表来进行查询。

4.如何优化sql

在Mysql原理与调优-索引原理及使用一文中,我们介绍了如何利用索引来优化单表,本小结在补充一下复杂查询的优化内容。

1. 对于排序分组等,尽量给排序字段或者分组字段建立索引,通过索引来实现分组和排序,减少filesort和temporary。

2.对于连接查询,尽量使用小表驱动大表,并且给连接字段建立索引。原因,前文已经分析过。

3.对于in和exists的选择,同连接查询类似。select A in(select B),会先执行B表查询,再执行A表查询,所以B表应该尽量小,并尽量给A表的过滤字段建立索引。select A exists (select B),会先执行A表,再到B中判断结果是否存在,所以A表应尽量小,同时给B表的连接字段建立索引。

3.查询时,尽量只查询需要查询的字段,原因是减少回表,尽量索引覆盖。

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

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

相关文章

作业帮 TiDB 7.5.x 使用经验

作者: 是我的海 原文来源: https://tidb.net/blog/5f9784d3 近期在使用 TiDB 时遇到的一些小问题的梳理总结,大部分版本都在6.5.6和7.5.2 1、limit 导致的扫描量过大的优化 研发定时任务每天需要扫描大量数据,到时机器网卡被…

26.10 Django Ajax异步提交

1. 表单提交 1.1 表单的作用 表单是Web开发中常见的数据收集方式, 它允许用户通过表单输入数据, 并通过提交操作将这些数据发送到服务器进行处理.表单提交方式主要分为两大类: 传统的同步提交(也称为标准提交)和异步提交(主要通过Ajax实现). 它们在工作方式, 用户体验和数据传…

语音助手Verbi:科技创新的未来

今天,我要向大家介绍一个名为Verbi的语音助手项目。这是一个结合了多种先进技术的模块化语音助手应用程序,能够实现语音到文本、文本生成和文本到语音的全流程处理。通过这个项目,我们可以体验到尖端科技如何改变我们的日常生活。 Verbi的诞…

PHP安全开发

安全开发 PHP 基础 增:insert into 表名(列名 1, 列名 2) value(‘列 1 值 1’, ‘列 2 值 2’); 删:delete from 表名 where 列名 ‘条件’; 改:update 表名 set 列名 数据 where 列名 ‘条件’; 查:select * from 表名 wher…

【STM32 Blue Pill编程】-外部中断配置及使用

外部中断配置及使用 文章目录 外部中断配置及使用1、中断介绍2、STM32中的中断3、硬件准备及接线4、GPIO配置5、代码实现在本文中,我们将介绍如何使用 STM32Cube IDE 中的 HAL 库配置和处理外部中断。 我们将通过一个带有按钮和 LED 的示例来演示这一点。 读完本文后,您将能够…

简单了解JVM执行Java程序的基本流程 | 一次编译,到处运行

前言: Java代码怎么做到一次编译,到处运行的呢?靠JVM,那JVM的执行流程是什么呢? 简单来说:通过Javac编译器将Java源代码编译成字节码,JVM通过类加载器将字节码加载到运行时数据区中,…

C++学习笔记之数据结构

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档 文章目录 一、二叉树1.1 二叉树的遍历1.1.1 深度优先搜索(DFS)1.1.2 广度优先搜索(BFS) 1.2 对称性(递归法&#x…

【论文笔记】LION: Linear Group RNN for 3D Object Detection in Point Clouds

原文链接:https://arxiv.org/abs/2407.18232 简介:Transformer在3D点云感知任务中有二次复杂度,难以进行长距离关系建模。线性RNN则计算复杂度较低,适合进行长距离关系建模。本文提出基于窗口的网络线性组RNN(即对分组…

Android 上下滑隐藏显示状态栏

一、DisplayPolicy类中监听滑动事件,然后发送广播事件 Android12类路径: frameworks/base/services/core/java/com/android/server/wm/DisplayPolicy.javamSystemGestures new SystemGesturesPointerEventListener(mUiContext, mHandler,new SystemGest…

《机器学习》周志华-CH1(绪论)

1.1引言 机器学习(Matchine-Learning)所研究的主要内容是关于在计算机上从数据中产生“模型”(model)的算法,即“学习算法”(learning algorithm)。可以说机器学习(Matchine-Learni…

2024最新最全面一线大厂面试Tips丨接口测试面试题

面试Tips# 面试是求职过程中至关重要的环节。在如今竞争激烈的就业市场,拥有优秀的面试技巧和良好的面试礼仪显得尤为重要,同样掌握这个的行业面试题也不可或缺。 01 你们公司的接口测试流程 是怎样的? (有没有感觉熟悉&#…

[Linux][OS][信号的捕捉] 可重入函数 | volatile | SIGCHLD信号

回顾:[Linux][OS][信号的保存和处理] 信号捕捉 1.sigaction int sigaction(int signo, const struct sigaction *act, struct sigaction *oact); 参数: signo:指定信号的编号act:输入型参数,根据act修改该信号的处…

水果甜度个人手持设备检测-(题外:为啥会选型这个课题)

系列:水果甜度个人手持设备检测 -- 题外:为啥会选型这个课题 写在前面的话 这段时间一直也在思考,在主业之外哪些方向和产业成熟度较高、技术复杂度又不是很离谱,比较容易出成果的方向,能够有空去试着做一做。这几年AI智能化正…

Java方法02:方法的定义和调用

本节内容视频链接:Java方法03:方法的重载_哔哩哔哩_bilibilihttps://www.bilibili.com/video/BV12J41137hu?p47&vd_sourceb5775c3a4ea16a5306db9c7c1c1486b5 在Java中,‌‌方法的定义是创建一段可重复使用的代码块的过程‌。‌它…

Java 开发者 LLM 实战:利用 LangChain4j 打造高效本地 RAG 系统

1、引言 由于目前比较火的chatGPT是预训练模型,而训练一个大模型是需要较长时间(参数越多学习时间越长,保守估计一般是几个月,不差钱的可以多用点GPU缩短这个时间),这就导致了它所学习的知识不会是最新的&…

代码随想录DAY17 - 二叉树 - 08/16

最大二叉树 题干 题目:给定一个不重复的整数数组 nums 。 最大二叉树 可以用下面的算法从 nums 递归地构建: 创建一个根节点,其值为 nums 中的最大值。 递归地在最大值 左边 的 子数组前缀上 构建左子树。 递归地在最大值 右边 的 子数组…

Linux下载卸载MySql

一. 安装Mysql 1.下载mysql --- 密钥 rpm --import https://repo.mysql.com/RPM-GPG-KEY-mysql-2022 rpm -ivh http://dev.mysql.com/get/mysql57-community-release-el7-10.noarch.rpm yum -y --enablerepomysql57-community install mysql-community-server 2.启动mysql sy…

如何选择最佳智能排班系统?9款工具全解析

本文介绍的自助排班管理工具有Moka、红圈排班、工作易排班、泛微e-office、Udesk、畅捷通T、Zenefits、Homebase、Deputy。 在管理团队时,手动排班不仅耗时,还容易出错,影响工作效率,相信很多管理者也有同样的困扰。为了解决这个痛…

【Spring Boot】定时任务

目录 前言 定时任务注解Scheduled 设计一个定时任务 1.启用定时任务 2.创建定时任务 Cron 表达式详解 多线程定时任务 总结 定时任务框架xxl-job SpringBoot继承定时任务框架 1.搭建调度中心xxl-job-admin 1.1下载项目 1.2修改配置文件端口和数据库代码 1.3连接到…

Linux进程间通信学习记录(无名管道)

0.Linux进程间通信的方式 (1).从UNIX继承过来的通信方式 无名管道(pipe) 有名管道(fifo) 信号(signal) (2).System V IPC 共享内存 消息队列 信号灯集 &am…