大纲

1.Explain概述

2.Explain详解

3.索引优化数据准备

4.索引优化原则详解

5.慢查询设置与测试

6.慢查询SQL优化思路

1.Explain概述

使用Explain关键字可以模拟查询优化器来执行SQL查询语句,从而知道MySQL是如何处理SQL语句的,从而分析出查询语句和表结构的性能瓶颈。

MySQL查询过程:

通过Explain可以获得以下信息:

一.表的读取顺序

二.数据读取操作的操作类型

三.哪些索引可以被使用

四.哪些索引真正被使用

五.表的直接引用

六.每张表的有多少行被优化器查询了

Explain使用方式:Explain + SQL语句,通过执行Explain可以获得SQL语句执行的相关信息。

EXPLAIN SELECT * FROM L1;

2.Explain详解

(1)数据准备

(2)ID字段说明

(3)select_type和table字段说明

(4)type字段说明

(5)possible_keys与key说明

(6)key_len字段说明

(7)ref字段说明

(8)rows字段说明

(9)filtered字段说明

(10)extra字段说明

(1)数据准备

-- 创建数据库
CREATE DATABASE test CHARACTER SET 'utf8'; -- 创建表
CREATE TABLE L1(id INT PRIMARY KEY AUTO_INCREMENT,title VARCHAR(100) );
CREATE TABLE L2(id INT PRIMARY KEY AUTO_INCREMENT,title VARCHAR(100) );
CREATE TABLE L3(id INT PRIMARY KEY AUTO_INCREMENT,title VARCHAR(100) );
CREATE TABLE L4(id INT PRIMARY KEY AUTO_INCREMENT,title VARCHAR(100) ); -- 每张表插入3条数据
INSERT INTO L1(title) VALUES('test001'),('test002'),('test003');
INSERT INTO L2(title) VALUES('test004'),('test005'),('test006');
INSERT INTO L3(title) VALUES('test007'),('test008'),('test009');
INSERT INTO L4(title) VALUES('test010'),('test011'),('test012');

(2)ID字段说明

ID字段代表SELECT查询的序列号,它是一组数字,表示的是查询或操作表的顺序。

一.ID相同,执行顺序就是由上至下

EXPLAIN SELECT * FROM  L1,L2,L3 WHERE L1.id=L2.id AND L2.id = L3.id;

二.ID不同,如果有子查询,ID号会递增

ID值越大优先级越高,越先被执行。

EXPLAIN SELECT * FROM L2 WHERE id = (
SELECT id FROM L1 WHERE id = (SELECT L3.id FROM L3 WHERE L3.title = 'test007')
);

(3)select_type和table字段说明

select_type表示查询类型,主要用于区别普通查询还是子查询等,table表示被操作的表。

一.SIMPLE:简单的SELECT查询,查询中不包含子查询或者UNION

EXPLAIN SELECT * FROM L1 where id = 1;

二.PRIMARY:在有子查询的情况下,最外层被标记为PRIMARY

三.SUBQUERY:在SELECT或WHERE列表中包含了子查询

EXPLAIN SELECT * FROM L2 WHERE id = (
SELECT id FROM L1 WHERE id = (SELECT L3.id FROM L3 WHERE L3.title = 'test08')
);

四.UNION:UNION连接的两个SELECT查询

在使用UNION时,左边的表的select_type是DERIVED,右边的表的select_type是UNION。

五.DERIVED:在FROM列表中包含的子查询被标记为DERIVED派生表

MySQL会递归执行这些被标记为DERIVED的子查询,然后把结果放到临时表中。

六.UNION RESULT:UNION的结果

EXPLAIN SELECT * FROM (SELECT * FROM L3 UNION SELECT * FROM L4) a;

(4)type字段说明

type字段表示的是连接类型,描述了找到所需数据而使用的扫描方式。

下面给出各种连接类型,按照从最好类型到最差类型进行排序:system -> const -> eq_ref -> ref -> fulltext -> ref_or_null -> index_merge -> unique_subquery -> index_subquery -> range -> index -> ALL

简化后,可以只关注以下几种 :system -> const -> eq_ref -> ref -> range -> index -> ALL

一般来说,需要保证查询至少达到range级别,最好能达到ref级别,否则就要就行SQL的优化调整。

下面介绍type字段不同值表示的含义:

一.system

表示表中仅有一行数据,这是const连接类型的一个特例,很少出现。

二.const

表示命中主键索引(primary key)或唯一索引(unique),通过主键索引或唯一索引一次就找到了数据。因为只匹配一条记录,所以被连接的部分是一个常量。如果将主键放在where条件中,MySQL就能将该查询转换为一个常量。这种类型非常快,例如以下查询:

EXPLAIN SELECT * FROM L1 WHERE id = 3;

-- 为L1表的title字段添加唯一索引
ALTER TABLE L1 ADD UNIQUE(title);
EXPLAIN SELECT * FROM L1 WHERE title = 'test001';

三.eq_ref

表示的是使用了唯一索引。比如连表查询中,对于前一个表中的每一行,后表只有一行被扫描。除了system和const类型之外,这是最好的连接类型。只有在连表时使用的索引都是主键或唯一索引时,才会出现这种类型,例如以下查询:

EXPLAIN SELECT L1.id,L1.title FROM L1 LEFT JOIN L2 ON L1.id = L2.id;

四.ref

表示使用了普通索引,即非唯一性索引。比如连表时对于前表的每一行,后表可能有多于一行的数据被扫描,例如以下查询:

-- 为L1表的title字段添加普通索引
ALTER TABLE L1 ADD INDEX idx_title (title);
EXPLAIN SELECT * FROM L1 INNER JOIN L2 ON L1.title = L2.title;
-- 如果L1表的title字段没有唯一索引,只有普通索引,如下查询也是ref
EXPLAIN SELECT * FROM L1 WHERE title = 'test001';

五.range

表示的是进行了索引上的范围查询,检索了给定范围的行,比如between、in函数、>都是典型的范围查询,例如以下查询:

EXPLAIN SELECT * FROM L1 WHERE L1.id BETWEEN 1 AND 10;

注意:当in函数中的数据很大时,可能会导致效率下降,最终不走索引。

六.index

当可以使用索引覆盖,但需要扫描全部索引记录时,则type为index。

当需要执行全表扫描,且需要对主键进行排序时,则type也为index。

所以如果type的值等于index,那么就需要进行优化了。因为出现index表示没有通过索引进行过滤,需要扫描索引的全部数据。index会遍历扫描索引树,比ALL快一些。如果索引文件过大,index的速度还是会很慢的。

总结:

当遍历二级索引不需要回表或者主键排序全表扫描时,type就为index。

注意:

使用索引进行排序分组时,可能会出现这种type值为index的情况。比如进行统计操作时,会出现type值为index的情况。

EXPLAIN SELECT * FROM L2 GROUP BY id ORDER BY id;
-- 该count查询需要通过扫描索引上的全部数据来计数
EXPLAIN SELECT count(*) FROM L2;

七.ALL

表示没有使用到任何索引,连表查询时对于前表的每一行,后表都要被全表扫描。

EXPLAIN SELECT * FROM L3 inner join L4 on L3.title = L4.title;

总结各类type类型的特点:

system:不进行磁盘IO,查询系统表,仅仅返回一条数据。

const:查找主键索引,最多返回1条或0条数据,属于精确查找。

eq_ref:查找唯一性索引,返回数据最多一条,属于精确查找。

ref:查找非唯一性索引,返回匹配的多条数据,属于精确查找。

range:查找索引中给定范围的行,属于范围查找(>、<、in、between)。

index:使用了索引但扫描全部了,比all快,因索引文件比数据文件小。

index:比如遍历二级索引不需要回表或者主键排序全表扫描。

all:不使用任何索引,直接进行全表扫描。

(5)possible_keys与key说明

一.possible_keys

表示可能用于查询的表上的索引。查询涉及到的字段上若存在索引,则该索引将被列出,但不一定被查询实际使用。

二.key

表示实际使用的索引。若为null,则表示没有使用到索引或索引失效。查询中若使用了覆盖索引,则该索引仅出现在key列表中。

情形一:理论和实际都用到了索引

EXPLAIN SELECT * FROM L1 WHERE id = 1;

情形二:理论上没有使用索引,但实际上使用了

EXPLAIN SELECT L3.id FROM L3;

情形三:理论和实际上都没有使用索引

EXPLAIN SELECT * FROM L3 WHERE title = 'test007';

(6)key_len字段说明

表示索引中使用的字节数,通过该列可以计算查询中使用索引的长度。key_len字段能够帮我们检查是否充分利用了索引,ken_len越长越好,说明索引使用的越充分。

一.创建表

CREATE TABLE L5(
a INT PRIMARY KEY,
b INT NOT NULL,
c INT DEFAULT NULL,
d CHAR(10) NOT NULL
);

二.使用EXPLAIN进行测试

-- 下面的查询只用到了主键a的索引
EXPLAIN SELECT * FROM L5 WHERE a > 1 AND b = 1;

观察key_len的值,用到了主键索引,是int类型的,所以key_len是4字节。

三.为b字段添加索引,进行测试

ALTER TABLE L5 ADD INDEX idx_b(b);
-- 执行SQL,这次将b字段也作为条件
EXPLAIN SELECT * FROM L5 WHERE a > 1 AND b = 1;

ken_len还是4。

四.为c、d字段添加联合索引,然后进行测试

ALTER TABLE L5 ADD INDEX idx_c_b(c,d);
EXPLAIN SELECT * FROM L5 WHERE c = 1 AND d = 'A';

c字段是int类型4个字节,d字段char(10)代表的是10个字符30个字节。因为数据库的字符集是utf8,一个字符3个字节。d字段是char(10)代表的是10个字符相当30个字节。多出的一个字节用来表示是联合索引。

下面这个例子虽然使用了联合索引,但没充分利用索引,还有优化空间。因为可以根据ken_len的长度推测出该联合索引只使用一部分。

EXPLAIN SELECT * FROM L5 WHERE c = 1;

(7)ref字段说明

表示的是显示索引的哪一列被使用了,如果可能的话,最好是一个常数。表示的是哪些列或常量被用于查找索引列上的值。

如下的"L1.id=1"中,由于1是常量,所以ref = const,此时的ref = const表示着查询过程中使用到了常量。

EXPLAIN SELECT * FROM L1 WHERE L1.id = 1;

(8)rows字段说明

表示MySQL为了找到所需的记录,一共访问了多少行(预估的)。L3中的title没有添加索引,所以L3中有3条记录,就需要访问3条记录。

EXPLAIN SELECT * FROM L3,L4 WHERE L3.id = L4.id AND L3.title LIKE 'test007';

需要注意的是rows只是一个估算值,并不准确。所以rows行数过大的问题并不值得过多考虑,主要分析的还是索引是否使用正确了。

(9)filtered字段说明

它指返回结果的行占需要读到的行(rows列的值)的百分比。

(10)extra字段说明

Extra是EXPLAIN输出中另外一个很重要的列,该列显示MySQL在查询过程中的一些详细信息。

一.准备数据

CREATE TABLE users (
uid INT PRIMARY KEY AUTO_INCREMENT,
uname VARCHAR(20),
age INT(11)
);
INSERT INTO users VALUES(NULL, 'lisa', 10);
INSERT INTO users VALUES(NULL, 'lisa', 10);
INSERT INTO users VALUES(NULL, 'rose', 11);
INSERT INTO users VALUES(NULL, 'jack', 12);
INSERT INTO users VALUES(NULL, 'sam', 13);

二.Using filesort(需要进行文件排序)

执行结果Extra为Using filesort,说明得到所需结果集,需要对所有记录进行文件排序。表示执行的SQL语句性能极差,需要进行优化。

下面就是在一个没有建立索引的列上进行order by,此时会触发filesort。优化方案是,在order by的列上添加索引,避免每次查询都全量排序。

EXPLAIN SELECT * FROM users ORDER BY age;

三.Using temporary

表示使用了临时表来存储结果集,常见于排序和分组查询。

EXPLAIN SELECT COUNT(*),uname FROM users GROUP BY uname;

四.Using where

表示使用了全表扫描或者在查找时使用索引的情况下,还有查询条件不在索引字段中需要回表。

注意一:返回所有记录的SQL,不使用where条件过滤数据,大概率不符合预期,这类SQL往往需要进行优化。

注意二:使用了where条件的SQL,并不代表不需要优化,往往需要配合explain结果中的type(连接类型)来综合判断。例如下面查询的age未设置索引,所以返回的type为ALL,仍有优化空间,可建立索引优化查询。

EXPLAIN SELECT * FROM users WHERE age = 10;

五.Using index

表示直接访问索引就能获取所需数据(覆盖索引),不需要回表。

-- 为uname创建索引
ALTER TABLE users ADD INDEX idx_uname(uname);
EXPLAIN SELECT uid,uname FROM users WHERE uname='lisa';

六.Using join buffer

表示使用了连接缓存,还会显示join连接查询时使用的算法。

EXPLAIN SELECT * FROM users u1 LEFT JOIN
(SELECT * FROM users WHERE age = 1) u2 ON u1.age = u2.age;

Using join buffer(Block Nested Loop)说明,需要进行嵌套循环计算。这里每个表都有五条记录,内外表查询的type都为ALL。两个表通过字段age进行关联,且age字段未建立索引。

七.Using index condition

表示的是使用了索引,但是只使用了索引的一部分。一般发生在使用联合索引时,需要回表查询。

EXPLAIN SELECT * FROM L5 WHERE c > 10 AND d = '';

八.Extra主要指标的含义总结

using index:查找时使用了覆盖索引的时候就会出现,不需要回表。

using where:查找时使用索引的情况下需要回表或全表扫描。

using index condition:查找时使用了索引但只用一部分索引需要回表。

Using filesort:在一个没有建立索引的列上order by,发生文件排序。

Using temporary:使用了临时表存储结果集,常见于排序和分组查询。

当遍历二级索引不需要回表或者主键排序全表扫描时,type就为index。

查找非唯一性索引,返回匹配的多条数据,type就为ref。

查找唯一性索引,返回匹配的数据最多一条,type就为eq_ref。

查找索引中给定范围的行,type就为range。

3.索引优化数据准备

(1)创建数据库、表,插入数据

create database idx_optimize character set 'utf8';
CREATE TABLE users(
id INT PRIMARY KEY AUTO_INCREMENT,
user_name VARCHAR(20) NOT NULL COMMENT '姓名',
user_age INT NOT NULL DEFAULT 0 COMMENT '年龄',
user_level VARCHAR(20) NOT NULL COMMENT '用户等级',
reg_time TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '注册时间'
);
INSERT INTO users(user_name,user_age,user_level,reg_time)
VALUES('tom',17,'A',NOW()),('jack',18,'B',NOW()),('lucy',18,'C',NOW());

(2)创建联合索引

ALTER TABLE users ADD INDEX idx_nal (user_name,user_age,user_level) USING BTREE;

4.索引优化原则详解

(1)最左侧列匹配和最左前缀匹配法则

(2)不要在索引列上做任何计算

(3)范围之后全失效

(4)避免使用is null、is not null、!= 、or

(5)like以%开头会使索引失效

(6)索引优化原则总结

(1)最左侧列匹配和最左前缀匹配法则

如果创建的是联合索引,就要遵循该法则。where后面的条件需从索引的最左侧列开始,且不能跳过索引中的列。如果where只匹配一个列,那么该列在索引最左侧,且只匹配前缀字段。

一.最左侧列匹配和最左前缀匹配的场景

场景1:按照索引字段顺序使用,三个字段都使用了索引,没有问题。

EXPLAIN SELECT * FROM users
WHERE user_name = 'tom' AND user_age = 17 AND user_level = 'A';

场景2:直接跳过user_name使用索引字段,索引无效,未使用到索引。

EXPLAIN SELECT * FROM users WHERE user_age = 17 AND user_level = 'A';

场景3: 不按照创建联合索引的顺序,使用索引。

EXPLAIN SELECT * FROM users WHERE user_age = 17 AND user_name = 'tom' AND user_level = 'A';

where后面查询条件顺序是user_age、user_level、user_name,这与创建的索引顺序user_name、user_age、user_level不一致。为什么还是使用了索引,原因是MySQL底层优化器对其进行了优化。

场景4:只要包含最左侧字段,索引就可以生效

但从key_len可知只是用到索引的一部分。

EXPLAIN SELECT * FROM users WHERE user_name = 'tom';

二.最左侧列匹配和最左前缀匹配的原理

InnoDB创建联合索引的规则是:

首先会对联合索引最左边的字段进行排序,例子中是user_name。在第一个字段的基础之上再对第二个字段进行排序,例子中是user_age。所以最佳左前缀原则其实是和B+树的结构有关系,最左字段肯定是有序的,第二个字段则是无序的。

联合索引的排序方式是:

先按第一个字段进行排序,如果第一个字段相等再根据第二个字段排序。所以如果直接使用第二个字段user_age通常是使用不到索引的。

(2)不要在索引列上做任何计算

不要在索引列上做任何操作,否则会导致索引失效,从而转向全表扫描。比如计算、使用函数、自动或手动进行类型转换(字符串不加双引号)。

一.插入数据

INSERT INTO users(user_name,user_age,user_level,reg_time) VALUES('11223344',22,'D',NOW());

场景1:使用系统函数left()函数,对user_name进行操作

EXPLAIN SELECT * FROM users WHERE LEFT(user_name, 6) = '112233';

场景2:字符串不加单引号(隐式类型转换)

对于varchar类型的字段,如果查询时不加单引号就会进行隐式转换,导致索引失效转向全表扫描。

EXPLAIN SELECT * FROM users WHERE user_name = 11223344;

(3)范围之后全失效

where条件中如果有范围条件,并且范围条件之后还有其他过滤条件,那么范围条件之后的列就都将会索引失效。

场景1:条件单独使用user_name时,type=ref、key_len=62。

-- 条件只有一个 user_name
EXPLAIN SELECT * FROM users WHERE user_name = 'tom';

场景2:条件增加一个user_age(使用常量等值),type= ref、key_len = 66。

EXPLAIN SELECT * FROM users WHERE user_name = 'tom' AND user_age = 17;

场景3:使用全值匹配,type = ref、key_len = 128,索引都利用上了。

EXPLAIN SELECT * FROM users WHERE user_name = 'tom' AND user_age = 17 AND user_level = 'A';

场景4:使用范围条件时,avg > 17、type = range、key_len = 66。与场景3比较,可发现user_level索引没用上。

EXPLAIN SELECT * FROM users WHERE user_name = 'tom' AND user_age > 17 AND user_level = 'A';

(4)避免使用is null、is not null、!= 、or

一.使用is null会使索引失效

EXPLAIN SELECT * FROM users WHERE user_name IS NULL;

Impossible Where:表示where条件不成立,不能返回任何行。

二.使用is not null会使索引失效

EXPLAIN SELECT * FROM users WHERE user_name IS NOT NULL;

三.使用!=和or会使索引失效

EXPLAIN SELECT * FROM users WHERE user_name != 'tom';
EXPLAIN SELECT * FROM users WHERE user_name = 'tom' or user_name = 'jack';

(5)like以%开头会使索引失效

一.like查询中%出现在左边则索引失效,%出现在右边索引未失效

场景1:两边都有%或者%在左边,索引都会失效

EXPLAIN SELECT * FROM users WHERE user_name LIKE '%tom%';
EXPLAIN SELECT * FROM users WHERE user_name LIKE '%tom';

场景2:%在右边,索引生效

EXPLAIN SELECT * FROM users WHERE user_name LIKE 'tom%';

二.解决%出现在左边索引失效的方法——使用覆盖索引

EXPLAIN SELECT user_name FROM users WHERE user_name LIKE '%jack%';
EXPLAIN SELECT user_name,user_age,user_level FROM users WHERE user_name LIKE '%jack%';

对比场景1可以知道:通过使用覆盖索引type = index,并且extra = Using index,从原来的全表扫描变成了全索引扫描,也就是索引的全表扫描。

三.like失效的原理

原理一:%号在右

由于B+树的索引顺序,是按照首字母的大小进行排序,而%号在右时的匹配又会匹配首字母,所以能在B+树上进行有序的查找。也就是查找出首字母符合要求的数据,所以%号在右可以用到索引。

原理二:%号在左是匹配字符串尾部的数据

由于尾部的字母是没有顺序的,所以不能按索引顺序查询,用不到索引。

原理三:两个%%号

这个是查询任意位置的字母满足条件即可。只有首字母是进行索引排序的,其他位置的字母都是相对无序的,所以查找任意位置的字母是用不上索引的。

(6)索引优化原则总结

一.最左侧列匹配和最左前缀匹配

二.索引列上不计算不转换

三.范围之后全失效

四.最好使用覆盖索引

五.!=、is null、is not null、or会索引失效

六.like百分号加右边,加左边导致索引失效的解决方法是使用覆盖索引

5.慢查询设置与测试

(1)慢查询介绍

(2)慢查询参数

(3)慢查询配置方式

(4)慢查询测试

(5)慢日志内容

(1)慢查询介绍

MySQL的慢查询全名是慢查询日志,是MySQL提供的一种日志记录。慢查询日志会记录在MySQL中响应时间超过阈值的语句。MySQL数据库默认不启动慢查询日志,需要手动来设置这个参数。

如果不是调优需要的话,一般不建议启动该参数。因为开启慢查询日志会或多或少带来一定的性能影响,慢查询日志支持将日志记录写入文件和数据库表。

(2)慢查询参数

执行下面的语句

mysql> show variables like '%slow_query_log%';
+---------------------+------------------------------+
| Variable_name | Value |
+---------------------+------------------------------+
| slow_query_log | ON |
| slow_query_log_file | /var/lib/mysql/test-slow.log |
+---------------------+------------------------------+ mysql> show variables like '%long_query%';
+-----------------+-----------+
| Variable_name | Value |
+-----------------+-----------+
| long_query_time | 10.000000 |
+-----------------+-----------+

MySQL慢查询的相关参数解释:

一.slow_query_log:是否开启慢查询日志。

二.slow-query-log-file:慢查询日志存储路径。

三.long_query_time:慢查询阈值,查询时间多于设定阈值则记录日志。

(3)慢查询配置方式

一.默认情况下slow_query_log的值为OFF,表示慢查询日志是禁用的

mysql> show variables like '%slow_query_log%';
+---------------------+------------------------------+
| Variable_name | Value |
+---------------------+------------------------------+
| slow_query_log | ON |
| slow_query_log_file | /var/lib/mysql/test-slow.log |
+---------------------+------------------------------+

二.可以通过设置slow_query_log的值来开启

mysql> set global slow_query_log=1;

三.set global slow_query_log=1开启慢查询日志当前生效重启失效

如果要永久生效,就必须修改配置文件my.cnf,其它系统变量也是如此。

-- 编辑配置
vim /etc/my.cnf -- 添加如下内容
slow_query_log =1
slow_query_log_file=/var/lib/mysql/ruyuan-slow.log -- 重启MySQL
service mysqld restart mysql> show variables like '%slow_query%';
+---------------------+--------------------------------+
| Variable_name | Value |
+---------------------+--------------------------------+
| slow_query_log | ON |
| slow_query_log_file | /var/lib/mysql/ruyuan-slow.log |
+---------------------+--------------------------------+

四. 开启了慢查询日志后,什么样的SQL才会记录到慢查询日志里

这个由参数long_query_time控制,默认long_query_time的值为10秒。

mysql> show variables like 'long_query_time';
+-----------------+-----------+
| Variable_name | Value |
+-----------------+-----------+
| long_query_time | 10.000000 |
+-----------------+-----------+ mysql> set global long_query_time=1;
Query OK, 0 rows affected (0.00 sec) mysql> show variables like 'long_query_time';
+-----------------+-----------+
| Variable_name | Value |
+-----------------+-----------+
| long_query_time | 10.000000 |
+-----------------+-----------+

五.修改变量long_query_time,但查询值还是10

执行命令set global long_query_time=1后,需要重新连接或者打开新开会话才能看到修改值。

mysql> show variables like 'long_query_time';
+-----------------+----------+
| Variable_name | Value |
+-----------------+----------+
| long_query_time | 1.000000 |
+-----------------+----------+

六.log_output参数是指定日志的存储方式

log_output=FILE表示将日志存入文件,默认值是FILE。log_output=TABLE表示将日志存入数据库,这样日志信息就会被写入到mysql.slow_log表中。

MySQL数据库可以同时支持两种日志存储方式,配置的时候以逗号隔开即可,如:log_output='FILE,TABLE'。

日志记录到系统的专用日志表中,要比记录到文件耗费更多的系统资源。因此如果启用慢查询日志+获得更高系统性能,则建议优先记录到文件。

mysql> SHOW VARIABLES LIKE '%log_output%';
+---------------+-------+
| Variable_name | Value |
+---------------+-------+
| log_output | FILE |
+---------------+-------+

七.开启系统变量让未使用索引的查询也被记录到慢查询日志中

这个系统变量就是log-queries-not-using-indexes,所以在进行调优时,可以开启这个选项。

mysql> show variables like 'log_queries_not_using_indexes';
+-------------------------------+-------+
| Variable_name | Value |
+-------------------------------+-------+
| log_queries_not_using_indexes | OFF |
+-------------------------------+-------+ mysql> set global log_queries_not_using_indexes=1;
Query OK, 0 rows affected (0.00 sec) mysql> show variables like 'log_queries_not_using_indexes';
+-------------------------------+-------+
| Variable_name | Value |
+-------------------------------+-------+
| log_queries_not_using_indexes | ON |
+-------------------------------+-------+

(4)慢查询测试

一.执行test_index.sql脚本,监控慢查询日志内容

[root@localhost mysql]# tail -f /var/lib/mysql/test-slow.log
/usr/sbin/mysqld, Version: 5.7.30-log (MySQL Community Server (GPL)). started with:
Tcp port: 0 Unix socket: /var/lib/mysql/mysql.sock
Time Id Command Argument

二. 执行下面的SQL,执行超时(超过1秒)我们去查看慢查询日志

SELECT * FROM test_index WHERE
hobby = '20009951' OR hobby = '10009931' OR hobby = '30009931'
OR dname = 'name4000' OR dname = 'name6600' ;

(5)慢日志内容

我们得到慢查询日志后,最重要的一步就是去分析这个日志。先来看慢日志里到底记录了哪些内容,如下是慢日志里其中一条记录,可以看到有时间戳、用户、查询时长及具体的SQL等信息。

Time                 Id Command    Argument
# Time: 2022-02-23 T03:55:15. 336037Z
# User@Host: root[root] @ localhost [] Id: 6
# Query_time: 2.375219 Lock_time: 0.000137 Rows_sent: 3 Rows_examined: 5000000
use db4;
SET timestamp=1645588515;
SELECT * FROM test_index WHERE hobby = '20009961' OR hobby = '10009941' OR hobby = '30009961' OR dname = 'name4001' OR dname = 'name6601';

Time:执行时间;

Users:用户信息;

Query_time:查询时长;

Lock_time:等待锁时长;

Rows_sent:结果行统计数量;

Rows_examined:扫描的行数;

6.慢查询SQL优化思路

(1)SQL性能下降的原因

(2)慢查询优化思路

(1)SQL性能下降的原因

导致SQL执行性能下降的原因可体现在以下两方面:

一.等待时间长

锁表导致查询一直处于等待状态。

二.执行时间长

查询语句没优化、索引失效、关联查询太多join、机器及参数没调优。

(2)慢查询优化思路

一.优先选择优化高并发执行的SQL

因为高并发的SQL出现问题带来后果更严重,比如下面两种情况:SQL1每小时执行10000次,每次20个IO,优化后每次18个IO,每小时节省2万次IO;SQL2每小时10次,每次20000个IO,每次优化减少2000个IO,每小时节省2万次IO。此时SQL2更难优化,SQL1更好优化。但是第一种属于高并发SQL,更急需优化,因为成本更低。

二.定位优化对象的性能瓶颈

在去优化SQL时,选择优化分方向有三个:

方向1:IO,数据访问消耗了太多时间,查看是否正确使用索引。

方向2:CPU,数据运算花费了太多时间,数据的运算分组、排序是不是有问题。

方向3:网络带宽,加大网络带宽。

三.明确优化目标

根据数据库当前状态、当前SQL的具体功能,来确定最好情况下消耗的资源和最差情况下消耗的资源。因为优化的结果只有一个,即给用户一个好的体验。

四.从explain执行计划入手

只有explain能告诉我们当前SQL的执行状态。

五.永远用小的结果集驱动大的结果集

小的数据集驱动大的数据集,减少内层表读取次数。

//类似于嵌套循环
for (int i = 0; i < 5; i++) {
for (int i = 0; i < 1000; i++) { }
}

六.尽可能在索引中完成排序

排序操作用得比较多,所以order by后面的字段尽量使用上索引。因为索引本来就是排好序的,所以速度很快。没有索引的话,就需要从表中拿数据,在内存中进行排序。如果内存空间不够还会发生临时文件落盘操作。

七.只获取自己需要的列

不要使用select *,因为select * 很可能不使用索引,而且数据量过大。

八.只使用最有效的过滤条件

where后面的条件并非越多越好,应该用最短的路径访问到数据。

九.尽可能避免复杂的join和子查询

每条SQL的JOIN操作建议不要超过三张表。将复杂的SQL,拆分成多个小的SQL,单个表执行,然后对获取的结果在程序中进行封装。因为如果join占用的资源比较多,会导致其他进程等待时间变长。

十.合理设计并利用索引

也就是合理判断是否需要创建索引,以及合理选择合适索引。

(3)如何判定是否需要创建索引

一.频繁作为查询条件的字段应该创建索引

二.唯一性太差的字段不适合单独创建索引,即使它频繁作为查询条件

唯一性太差的字段主要是指哪些呢?如状态字段、类型字段等。这些字段中的数据可能总共就是那么几个几十个数值重复使用。当一条Query所返回的数据超过了全表的15%时,就不应该再使用索引扫描来完成这个Query了。

三.更新非常频繁的字段不适合创建索引

因为索引中的字段被更新时,不仅要更新表的数据,还要更新索引数据。

四.不会出现在WHERE子句中的字段不该创建索引

(4)如何选择合适索引

一.单键索引,尽量选择针对当前Query过滤性更好的索引。

二.联合索引,当前查询中过滤性最好的字段在索引字段顺序中排列靠前。

MySQL底层概述—7.优化原则及慢查询的更多相关文章

  1. mysql查询优化之四:优化特定类型的查询

    本文将介绍如何优化特定类型的查询. 1.优化count()查询count()聚合函数,以及如何优化使用了该函数的查询,很可能是mysql中最容易被误解的前10个话题之一 count() 是一个特殊的函 ...

  2. java架构之路-(mysql底层原理)Mysql之让我们再深撸一次mysql

    让我再深撸一次mysql吧,这次主要以应对面试来说说mysql,大概几个方向,索引结构,查询引擎,索引优化,explain的详解和trace工具的使用. 索引: 我们先来看一下mysql的B+tree ...

  3. 我的mysql数据库sql优化原则

    原文 我的mysql数据库sql优化原则 一.前提 这里的原则 只是针对mysql数据库,其他的数据库 某些是殊途同归,某些还是存在差异.我总结的也是mysql普遍的规则,对于某些特殊情况得特殊对待. ...

  4. MySQL 索引优化原则

    一.索引优化原则 1.最左前缀匹配原则,联合索引,mysql会从做向右匹配直到遇到范围查询(>.<.between.like)就停止匹配,比如a = 1 and b = 2 and c & ...

  5. mysql数据库优化原则

    一.一个例子 数据库需要处理的行数: 189444*1877*13482~~~479亿 如果在关联字段上加上合适的索引: 数据库需要处理的行数:368006*1*3*1~~~110万 MySQL通常是 ...

  6. mysql语句优化原则

    有时候发现数据量大的时候查询起来效率就比较慢了,学习一下mysql语句优化的原则,自己在正常写sql的时候还没注意到这些,先记录下来,慢慢一点一点的学,加油! 这几篇博客写的都可以: https:// ...

  7. MYSQL慢查询优化方法及优化原则

    1.日期大小的比较,传到xml中的日期格式要符合'yyyy-MM-dd',这样才能走索引,如:'yyyy'改为'yyyy-MM-dd','yyyy-MM'改为'yyyy-MM-dd'[这样MYSQL会 ...

  8. [MySQL Reference Manual] 8 优化

    8.优化 8.优化 8.1 优化概述 8.2 优化SQL语句 8.2.1 优化SELECT语句 8.2.1.1 SELECT语句的速度 8.2.1.2 WHERE子句优化 8.2.1.3 Range优 ...

  9. SQL优化 MySQL版 - 多表优化及细节详讲

    多表优化及细节详讲 作者 : Stanley 罗昊 [转载请注明出处和署名,谢谢!] 注:本文章需要MySQL数据库优化基础或观看前几篇文章,传送门: B树索引详讲(初识SQL优化,认识索引):htt ...

  10. Lucene底层原理和优化经验分享(1)-Lucene简介和索引原理

    Lucene底层原理和优化经验分享(1)-Lucene简介和索引原理 2017年01月04日 08:52:12 阅读数:18366 基于Lucene检索引擎我们开发了自己的全文检索系统,承担起后台PB ...

随机推荐

  1. Angular Material 18+ 高级教程 – Material Icon

    前言 不熟悉 Icon 的可以先看这篇 CSS – Icon. Material Design 有自己的一套 Icon 设计.Angular Material 默认就使用这一套. 不过呢,目前 v17 ...

  2. OData – Routing

    前言 以前我都是把 ODataController 和普通 API Controller 分开做. (因为 OData 实在多 Bug, 好东西尽量不要掺和它) Read 的部分用 OData, CU ...

  3. 29. GIL全局解释器锁、信号量、线程池进程池

    1. GIL全局解释器锁 1.1 概念 '''In CPython, the global interpreter lock, or GIL, is a mutex that prevents mul ...

  4. NICE与静态优先级的关系

    在Linux系统中,nice值和静态优先级用于控制进程调度的优先级,但它们的范围和含义有所不同.让我们详细解释一下两者的区别和联系. 1. Nice值 范围:nice值的范围是从 -20 到 19. ...

  5. CocoaPods常用的命令行以及安装方法

    1.新建一个Xcode工程,使用终端cd到工程目录下 2.创建Podfile文件 pod init ,之后就可以在项目目录里看到一个Podfile文件 3.打开Podfile文件:open Podfi ...

  6. 激活windows教程

    新建bat文件 [批处理文件:后缀是 bat ] 输入代码: slmgr/skms kms.03k.org slmgr/ato 然后以管理员运行 :

  7. javascript 中 0.1 + 0.2 === 0.3 是否正确 ?

    不正确 ,因为 js 是 动态 .弱类型 ,即时编译的语言 :js中的小数都是浮点型 ,比如 0.1 实际上可能是 0.11111111... 0.2 可能是 0.2 22222..... 所以 0. ...

  8. Vue3 和 Vue2 的区别 ?

    1. Vue3 和 VUe2 性能提升 :使用 proxy 代替 defainProperty 实现响应式数据 :使用 ts 书写代码 : 新特性有:组合 api compositionApi  :新 ...

  9. Termux 使用笔记

    第一次安装完成后 发现这里面啥都没有 所以 更新源 apt update 也做不到 这是因为 源是国外 由于有墙 所以连接不上 下面这个命令可以 切换更新源 termux-change-repo 切换 ...

  10. KubeSphere Meetup 北京站火热报名中 | 搭载 CIC 2021 云计算峰会

    "CIC 2021 云计算峰会"是一场 Top 级行业盛会,将汇聚 800 家青云QingCloud 企业客户信息化负责人,采取线上线下相结合的方式,与会规模将超过 10000 人 ...