百度360必应搜狗淘宝本站头条
当前位置:网站首页 > IT技术 > 正文

MySQL 从入门到精通(六) SQL性能分析及索引使用规则

wptr33 2025-04-08 19:43 14 浏览

SQL性能分析工具有助于我们能够快速定位哪些SQL语句执行效率低下,从而有针对性的进行优化,这里我们优化的主要语句是SQL的DQL语句也就是查询语句。而在优化select查询语句的时候呢,索引的优化占据相当高的比重。

首先我们得知道MySQL服务器中SQL语句的执行频率如何,可通过show [sessison|global] status 命令查服务器的状态,然后通过show global status like 'Com_______'; 查看当前数据库的insert、update、delete、select的访问频次。

show global status like 'Com_______'; #匹配后面的7个字符串

mysql> show global status like 'Com_______';
+---------------+-------+
| Variable_name | Value |
+---------------+-------+
| Com_binlog | 0 |
| Com_commit | 0 |
| Com_delete | 0 |      # 删除
| Com_insert | 0 |       # 插入
| Com_repair | 0 |
| Com_revoke | 0 |     
| Com_select | 4 |       # 查询
| Com_signal | 0 |
| Com_update | 0 |     # 更新
| Com_xa_end | 0 |
+---------------+-------+
10 rows in set (0.00 sec)

#  当有增删改查的操作时 对应value会改变
mysql> show global status like 'Com_______';
+---------------+-------+
| Variable_name | Value |
+---------------+-------+
| Com_binlog    | 0     |
| Com_commit    | 0     |
| Com_delete    | 0     |
| Com_insert    | 1     |
| Com_repair    | 0     |
| Com_revoke    | 0     |
| Com_select    | 3023  |
| Com_signal    | 0     |
| Com_update    | 0     |
| Com_xa_end    | 0     |
+---------------+-------+
10 rows in set (0.01 sec)

通过这条语句可以判断出当前数据库是以查询为主还是以写入为主,如果一个数据库它查询占据了绝大部分,那么此时我们就要针对这类的数据库当中的SQL来进行优化了。好了,这里是我们介绍的第一种SQL性能分析工具,通过这条指令来查看SQL的执行频率,为我们的SQL优化提供支撑。

第二种、慢查询日志

由于第一种情况只能知道当前数据库的select执行频率,并不知道具体要对哪些select语句进行优化,此时我们就要通过MySQL数据库的慢查询日志来定位哪些SQL语句执行效率比较低,从而对这类SQL语句进行优化。

慢查询日志记录了所以执行时间超过指定参数(long_query_time,单位:秒,默认10秒)的所有SQL语句的日志,MySQL的慢查询日志默认是没有开启的,需要在my.cnf配置文件中添加如下信息:

#开启MySQL慢日志查询开关

slow_query_log=1

#设置慢日志的时间为2秒,SQL语句执行时间超过2秒,就会视为慢查询,记录到文件里

long_query_time=2

配置完毕之后,通过以下命令重启MySQL进行测试,查看慢日志文件中记录的信息。

或者set global slow_query_log=ON; set global long_query_time=2;无需重启。

mysql> show global variables like 'long_query_%';
+-----------------+----------+
| Variable_name | Value |
+-----------------+----------+
| long_query_time | 0.100000 |
+-----------------+----------+
1 row in set (0.02 sec)

mysql> show global variables like 'slow_query_%';
+---------------------+----------------------------------+
| Variable_name | Value |
+---------------------+----------------------------------+
| slow_query_log | ON |
| slow_query_log_file | /data/mydata/mdata/logs/slow.log |
+---------------------+----------------------------------+
2 rows in set (0.01 sec)

tail -n 20 /data/mydata/mdata/logs/slow.log
select * from king_user;
# Time: 2022-04-10T10:02:40.254528Z
# User@Host: root[root] @ localhost []  Id:  1002
# Query_time: 0.000440  Lock_time: 0.000140 Rows_sent: 10  Rows_examined: 10
SET timestamp=1649584960;
select * from king_user;
/data/soft/mysql/bin/mysqld, Version: 5.7.32-log (MySQL Community Server (GPL)). started with:
Tcp port: 3306  Unix socket: /tmp/mysql.sock
Time                 Id Command    Argument
# Time: 2022-04-11T06:55:03.673298Z
# User@Host: root[root] @ localhost []  Id:     2
# Query_time: 0.058483  Lock_time: 0.000569 Rows_sent: 458  Rows_examined: 458
use xjqx_game_s410;
SET timestamp=1649660103;
select * from bag;
# Time: 2022-04-11T06:57:15.320498Z
# User@Host: root[root] @ localhost []  Id:     2
# Query_time: 0.010096  Lock_time: 0.000155 Rows_sent: 458  Rows_examined: 458
SET timestamp=1649660235;
select * from yy_vip;

第三种,profile详情

有些查询日志非常接近我们设置的慢日志的时间,这一类日志的执行效率也是很低的,但是没有被记录下来,这一类日志也是要优化的。如何定位这一类日志呢?我们可以借助profile详情,show profiles指令能够在做SQL优化的时候帮助我们了解时间都耗费到拿了,通过have_profiling参数,能够看到当前MySQL是否支持profile操作。

mysql> select @@have_profiling;
+------------------+
| @@have_profiling |
+------------------+
| YES |
+------------------+
1 row in set, 1 warning (0.02 sec)

默认profiling是关闭的,可以通过set语句在session/global级别开启profiling

mysql> show variables like 'profi%';
+------------------------+-------+
| Variable_name | Value |
+------------------------+-------+
| profiling | OFF |
| profiling_history_size | 15 |
+------------------------+-------+
2 rows in set (0.01 sec)

mysql> select @@profiling;
+-------------+
| @@profiling |
+-------------+
|           0 |
+-------------+
1 row in set, 1 warning (0.02 sec)

set profiling=1;

mysql> set profiling=1;
Query OK, 0 rows affected, 1 warning (0.00 sec)
mysql> show variables like 'profi%';
+------------------------+-------+
| Variable_name | Value |
+------------------------+-------+
| profiling | ON |
| profiling_history_size | 15 |
+------------------------+-------+
2 rows in set (0.01 sec)

我先执行一些select语句

select * from xjqx_game_s410.account

select * from king_user where name = '石秀';

select * from king_user where id = 10;

通过 show profiles指令可以看到查询语句所耗费的时间,同时也看出根据id查询的效率比根据name查询的效率高出很多。为什么,不了解的可以回顾上一节的内容。

show profiles # 是查看每一条SQL指令的耗时时间。

show profile for query Query_ID # 查看指定id的SQL语句各个阶段的耗时情况

mysql> show profile for query 4;
+----------------------+----------+
| Status | Duration |
+----------------------+----------+
| starting | 0.000175 |
| checking permissions | 0.000022 |
| Opening tables | 0.000108 |
| init | 0.000057 |
| System lock | 0.000023 |
| optimizing | 0.000010 |
| statistics | 0.000024 |
| preparing | 0.000022 |
| executing | 0.000006 |
| Sending data | 0.054713 |    # 发送数据这一块比较耗时
| end | 0.000033 |
| query end | 0.000018 |
| closing tables | 0.000022 |
| freeing items | 0.000072 |
| logging slow query | 0.000153 |
| cleaning up | 0.000036 |
+----------------------+----------+
16 rows in set, 1 warning (0.00 sec)
mysql>

show profile cpu for query Query_ID #查看指定ID 的SQL语句的CPU使用情况

第四种、explain执行计划

以上三种都是通过时间的评判一条SQL语句的性能,执行时间短说明SQL语句的性能高反之低,这种判定只能算是粗略的,并不能真正的评判一条SQL语句的性能。我们要想看一条SQL语句的性能还需要借助explain来查看SQL的执行计划。explain或者desc命令可以获取mysql如何执行select语句的信息,包括在select语句执行过程中表是如何连接和连接的顺序。语法如下:

explain/desc select 字段列表 from 表名 where 条件;

desc select * from king_user where name='糜夫人';

explain select * from bag where id=4100000027;

explain执行计划个列含义

id: select查询的序号,表示查询中执行select子句或者是操作的顺序,id相同的情况下从执 行顺序从上到下,值越大越先执行.

select * from student as s,caurse c,student_caurse sc where sc.id=c.id and c.id=sc.id;

explain select * from student as s where s.id in (select stu_id from student_caurse as sc where caur_id=(select id from caurse as c where c.id=3));

select_type:表示select的类型,simple简单表(即不使用表连接和子查询),primary(主查询,即外层查询),union(union中的第二个或者后面的查询语句),subquery(select/where之后包含了子查询)等。

type: 表示连接类型,性能由好到差的连接类型为NULL、system、const、eq_ref、ref、range、index、all当我们根据主键或者唯一索引查询时 type为const


非唯一性索引为:ref

不走索引的情况下是all 全表扫描

possible_key 表示可能应用到这张表上的索引,一个或者多个。

key 显示实际用到的索引,没有则NULL

key_len 表示索引用到的字节数,该值为索引字段最大可能长度,并非实际使用长度,在不 损失精度的前提下,长度越短越好。

rows MySQL认为必须要执行的行数,在innodb引擎中,是一个估计值,可能并不总是标准 的。

filtered 表示返回结果的行数占需读取行数的百分比,filtered的值越大越好。

Extra 额外的信息

索引使用规则

索引的最左前缀法则,如果索引关联了多列(联合索引),要遵守最左前缀法则,最左前缀法则指的是查询从索引的最左边列开始,并且不跳过索引中的列。如果跳跃某一列,索引将部分失效(后面的字段索引将失效)

索引的最左边字段必须存在,和字段的顺序无关。

联合索引


desc select * from king_user where profession='如雷三叉戟' and age=23 and status=1;

desc select * from king_user where profession='如雷三叉戟' and age=23;

desc select * from king_user where profession='如雷三叉戟';

desc select * from king_user where age=23 and status=1; #不走索引

跳过age字段,后面的字段status索引不生效

2、当使用了范围查询时(> <),右边的列索引失效,尽量使用>= <= =。

3、不要在索引列上进行运算操作,索引将失效。

select * from king_user where substring(phone,10,2)='16'; #截取后两位

4、字符串类型的字段,如果不加引号,索引失效。

5、模糊查询,如果仅仅是尾部模糊匹配,索引是不会失效的,如果是头部模糊匹配索引将失效。 如 '%log',切记大数量时不能这么玩,会全表扫描。

6、or连接的条件,如果用or分割开的条件,or前的条件中的列有索引,后面的列没有索 引,那么涉及到的所有都不会用到。

desc select * from king_user where id=9 or age=25;

desc select * from king_user where age=25 or phone='13056980266';

由于age没有索引,所以即使id、phone有索引,索引也会失效,要对age建立索引才行。

create index idx_usr_age king_user(age)

7、数据分布影响,如果MySQL评估使用索引比全表扫描更慢,则不使用索引。

is not null/is null 同样根据数据分布进行评估。

8、使用SQL提示,规定使用哪个索引

use index:

desc select * from king_user use index(idx_user_pro) where profession='张飞';

ignore index:

desc select * from king_user ignore index(idx_user_pro) where profession='张飞';

force index:

desc select * from king_user force index(idx_user_pro) where profession='张飞';

。。。。。

9、覆盖索引,就是尽量使用索引列,减少使用select *.

desc select id,age,profession from king_user where profession='张飞' and age=31 and status=1;

如果执行计划的Extra是:

a、Extra: Using where; Using index 表示查找使用了索引,但是需要的数据都在索引 列中能找到,所以不需要回表查询,效率高。

select id,name from king_user where name='Arm';

b、Extra: Using index condition 查找使用了索引,但是需要回表查询数据。

select id,name,gender from king_user where name='Arm'; # gender 没有找到,根据id 值回表查询,找到这行数据再提起gender值返回。所以说使用select * 很容易就出现回 表查询,除非你创建索引字段的联合索引,否则改回哪回哪查,导致性能降低。

面试题: 有以下语句,请问如何创建索引,使查询最优?

select id,username,password from king_user where username='张三';

根据二级索引的特点,叶子节点存放的是id值,可以考虑给username和password建立 联合索引,查询列都是索引列,所以不会回表查询。

前缀索引

当字段类型为字符串varchar、text时,有时候需要索引很长的字符串,这样会使索引变得很大,查询时浪费大量的磁盘IO,影响查询效率。此时可以只将字符串的一部分前缀,建立索引,这样可以大大节约索引空间,从而提高效率。

语法: create index idx_xxxx on 表名(column(n));

如何确定前缀的长度,可以根据索引的选择性来决定,而选择性是指不重复的索引值和数据表的记录总数的比值,索引的选择性越高查询效率越高。

唯一索引的选择性是1,这是最好的索引选择性,性能也是最好。

select count(distinct email)/count(*) from king_user;

select count(distinct substring(email,1,5))/count(*) from king_user;

如下 分别截取3,2,1个字符作为前缀的话不是最好的

此处我们截取5个字符作为前缀索引

create index idx_email_5 on king_user(email(5));

查看执行计划也是用到索引的

实际生产环境中如果使用了长字符串或者大文本字段,我们就可以使用前缀索引来缩小索引的体积。(前缀索引需要回表查询)


单列索引与联合索引的选择

单列索引,即一个索引只包含单个字段。(如果查询中包含多个单列查询,则MySQL会自己判断哪个索引最优然后选择它,剩下的列即使是有索引的也无效。导致回表查询影响性能。)

提示: 在多条件联合查询时,MySQL优化器会评估哪个字段的索引效率更高,会选择该字段完成本次查询。

联合索引,一个索引包含多个字段。

在实际业务场景中,如果存在多个查询条件,考虑针对于查询字段建立的索引时,建议建立联合索引,而非单列索引。

索引的设计原则

1、针对那些数据量比较大,且查询比较频繁的表建立索引。

2、针对于经常作为查询条件where、排序order by、分组 group by操作的字段建立索引。

3、尽量选择区分度高的列作为索引,尽量建立唯一索引,区分度也高,使用索引的效率就 越高。

4、如果是字符串类型的字段,字段的长度较长,可以针对字段的特点建立前缀索引。

5、尽量使用联合索引,减少单列索引,查询时,联合索引很多时候可以覆盖索引,节省存 储空间避免回表查询,提高查询效率。

6、要控制索引的数量,索引并不是越多越好,索引过多维护索引的结构的代价会越来越 大,会影响增删改的效率。

7、如果索引列不能存储NULL值,请在创建索引时用not null约束它,当优化器知道每列是否 包含null时,它可以更好的确定哪个索引最有效地用于查询。

相关推荐

Linux高性能服务器设计

C10K和C10M计算机领域的很多技术都是需求推动的,上世纪90年代,由于互联网的飞速发展,网络服务器无法支撑快速增长的用户规模。1999年,DanKegel提出了著名的C10问题:一台服务器上同时...

独立游戏开发者常犯的十大错误

...

学C了一头雾水该咋办?

学C了一头雾水该怎么办?最简单的方法就是你再学一遍呗。俗话说熟能生巧,铁杵也能磨成针。但是一味的为学而学,这个好像没什么卵用。为什么学了还是一头雾水,重点就在这,找出为什么会这个样子?1、概念理解不深...

C++基础语法梳理:inline 内联函数!虚函数可以是内联函数吗?

上节我们分析了C++基础语法的const,static以及this指针,那么这节内容我们来看一下inline内联函数吧!inline内联函数...

C语言实战小游戏:井字棋(三子棋)大战!文内含有源码

井字棋是黑白棋的一种。井字棋是一种民间传统游戏,又叫九宫棋、圈圈叉叉、一条龙、三子旗等。将正方形对角线连起来,相对两边依次摆上三个双方棋子,只要将自己的三个棋子走成一条线,对方就算输了。但是,有很多时...

C++语言到底是不是C语言的超集之一

C与C++两个关系亲密的编程语言,它们本质上是两中语言,只是C++语言设计时要求尽可能的兼容C语言特性,因此C语言中99%以上的功能都可以使用C++完成。本文探讨那些存在于C语言中的特性,但是在C++...

在C++中,如何避免出现Bug?

C++中的主要问题之一是存在大量行为未定义或对程序员来说意外的构造。我们在使用静态分析器检查各种项目时经常会遇到这些问题。但正如我们所知,最佳做法是在编译阶段尽早检测错误。让我们来看看现代C++中的一...

ESL-通过事件控制FreeSWITCH

通过事件提供的最底层控制机制,允许我们有效地利用工具箱,适时选择使用其中的单个工具。FreeSWITCH是一个核心交换与混合矩阵,它周围有几十个模块提供各种功能特性。我们完全控制了所有的即时信息,这些...

物理老师教你学C++语言(中篇)

一、条件语句与实验判断...

C语言入门指南

当然!以下是关于C语言入门编程的基础介绍和入门建议,希望能帮你顺利起步:C语言入门指南...

C++选择结构,让程序自动进行决策

什么是选择结构?正常的程序都是从上至下顺序执行,这就是顺序结构...

C++特性使用建议

1.引用参数使用引用替代指针且所有不变的引用参数必须加上const。在C语言中,如果函数需要修改变量的值,参数必须为指针,如...

C++程序员学习Zig指南(中篇)

1.复合数据类型结构体与方法的对比C++类:...

研一自学C++啃得动吗?

研一自学C++啃得动吗?在开始前我有一些资料,是我根据网友给的问题精心整理了一份「C++的资料从专业入门到高级教程」,点个关注在评论区回复“888”之后私信回复“888”,全部无偿共享给大家!!!个人...

C++关键字介绍

下表列出了C++中的常用关键字,这些关键字不能作为变量名或其他标识符名称。1、autoC++11的auto用于表示变量的自动类型推断。即在声明变量的时候,根据变量初始值的类型自动为此变量选择匹配的...