MySQL索引优化(二)

一、查询优化

1. 索引失效

(1)最左前缀法则:如果索引有多列,要遵循最左前缀法则,指的是查询从索引的最左前列开始并且不跳过索引中的列。(针对复合索引)
(2)不在索引列上做任何计算、函数操作,会导致索引失效而转向全表扫描。
(3)存储引擎不能使用索引中范围条件右边的列。
(4)Mysql在使用不等于时无法使用索引,会导致全表扫描。
(5)is null可以使用索引,但是is not null无法使用索引。
(6)like以通配符开头会使索引失效,导致全表扫描。
(7)字符串不加单引号,索引会失效。
(8)使用or连接时,索引失效。

  • 创建表
drop table if exists students;
CREATE TABLE students (
  id INT PRIMARY KEY AUTO_INCREMENT COMMENT "主键id",
  sname VARCHAR (24) COMMENT '学生姓名',
  age INT COMMENT '年龄',
  score INT COMMENT '分数',
	time TIMESTAMP COMMENT '入学时间'
);
 
INSERT INTO students(sname,age,score,time) VALUES('小明',22,100,now());
INSERT INTO students(sname,age,score,time) VALUES('小红',23,80,now());
INSERT INTO students(sname,age,score,time) VALUES('小绿',24,80,now());
INSERT INTO students(sname,age,score,time) VALUES('黑',23,70,now());
  • 创建复合索引
alter table students add index idx_sname_age_score(sname,age,score);

在这里插入图片描述

  • 使用复合索引查询

key_len表示使用索引的字节大小,值越大表示索引使用越充分

explain select * from students where sname="小明" and age = 22 and score = 100;
explain select * from students where sname="小明" and age = 22;
explain select * from students where sname="小明";

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

(1)不满足最左前缀法则,索引失效

explain select * from students where age = 20;

在这里插入图片描述

查询条件是sname、score,中间跳过了age, 所以score没有用到索引,只有sname用到了索引
跟上面单独使用sname查询时的ken_len一样,都是99

explain select * from students where sname="小明" and score = 80;

在这里插入图片描述

(2)在索引列上做任何计算、函数操作,索引失效

-- 不在索引列上做任何计算、函数操作,会导致索引失效而转向全表扫描。
explain select * from students where left(sname,2) = "小明";

在这里插入图片描述

(3)存储引擎使用索引中范围条件右边的列,索引失效

age是范围查找,所以age右边的score列索引失效,ken_len=104说明只有sname、age列索引生效

-- 存储引擎不能使用索引中范围条件右边的列。
explain select * from students where sname="小明" and age > 22 and score = 100;

在这里插入图片描述

(4)Mysql使用不等于时,索引失效

-- Mysql在使用不等于时无法使用索引会导致全表扫描。
explain select * from students where sname != "小明";

在这里插入图片描述

(5)is null可以使用索引,但是is not null无法使用索引

explain select * from students where sname is  null;

在这里插入图片描述

-- is null可以使用索引,但是is not null无法使用索引。
explain select * from students where sname is not null;

在这里插入图片描述

(6)like以通配符开头,索引失效

-- like以通配符开头会使索引失效导致全表扫描。
explain select * from students where sname like "%明";

在这里插入图片描述

(7)字符串不加单引号,索引失效

-- 字符串不加单引号索引会失效。
explain select * from students where sname = 123;

在这里插入图片描述

(8)使用or连接时,索引失效

-- 使用or连接时索引失效。
explain select * from students where sname="小明" or age = 22;

在这里插入图片描述

小结

在这里插入图片描述

建议:
1.对于单值索引,尽量选择针对当前查询字段过滤性更好的索引。
2.对于组合索引,当前where查询中过滤性更好的字段在索引字段顺序中位置越靠前越好。
3.对于组合索引,尽量选择能够包含在当前查询中where子句中更多字段的索引。
4.尽可能通过分析统计信息和调整query的写法来达到选择合适索引的目的。

2. 排序优化

1.尽量避免使用Using FileSort方式排序。
2.order by语句使用索引最左前列或使用where子句与order by子句条件组合满足索引最左前列。
3.where子句中如果出现索引范围查询会导致order by索引失效。

KEY a_b_c(a,b,c)
order by 能使用索引最左前缀
-- ORDER BY a
-- ORDER BY a,b
-- ORDER BY a,b,c
-- ORDER BY a DESC, b DESC, c DESC

如果WHERE使用索引的最左前缀定义为常量,则order by能使用索引
-- WHERE a = CONST ORDER BY b,c 
-- WHERE a = CONST AND b = CONST ORDER BY c 
-- WHERE a = CONST AND b > CONST ORDER BY b,c 

不能使用索引进行排序
-- ORDER BY a ASC, b DESC, c DESC // 排序不一致
-- WHERE g = CONST ORDER BY b,c // 丢失a索引
-- WHERE a = CONST ORDER BY c  // 丢失b索引
-- WHERE a = CONST ORDER BY a,d  // d不是索引的一部分
-- WHERE a in(...) ORDER BY b,c //对于排序来说,多个相等条件也是范围查询

3. 单表优化

  • 建表
-- 单表查询优化
CREATE TABLE IF NOT EXISTS article (
id INT(10) PRIMARY KEY AUTO_INCREMENT,
author_id INT(10) NOT NULL,
category_id INT(10) NOT NULL,
views INT(10) NOT NULL,
comments INT(10) NOT NULL,
title VARBINARY(255) NOT NULL,
content TEXT NOT NULL
);
 
INSERT INTO article(author_id, category_id, views, comments, title, content) VALUES
(1, 1, 1, 1, '1', '1'),
(2, 2, 2, 2, '2', '2'),
(1, 1, 3, 3, '3', '3');
#1.查询category_id为1的,且comments大于1的情况下,views最多的id和author_id的信息
explain select id,author_id 
from article
where category_id=1 and comments>1 order by views desc limit 1;

在这里插入图片描述

#2.建立索引
alter table article add index idx_ccv(category_id,comments,views);
#3.再次测试,comments是范围查询,所以右边views列索引失效
explain select id,author_id 
from article
where category_id=1 and comments>1 order by views desc limit 1;

在这里插入图片描述

#4.重新创建索引
drop index idx_ccv on article;
// comments是范围查询,所以不建立comments复合索引
alter table article add index idx_cv(category_id,views);
#5.再次测试
explain select id,author_id 
from article
where category_id=1 and comments>1 order by views desc limit 1;

在这里插入图片描述

4. 关联查询优化

  • 建表
CREATE TABLE IF NOT EXISTS class (
id INT(10) AUTO_INCREMENT,
card INT(10),
PRIMARY KEY (id)
);
CREATE TABLE IF NOT EXISTS book (
bookid INT(10) AUTO_INCREMENT,
card INT(10),
PRIMARY KEY (bookid)
);
 
INSERT INTO class(card) VALUES(FLOOR(1 + (RAND() * 20)));
INSERT INTO class(card) VALUES(FLOOR(1 + (RAND() * 20)));
INSERT INTO class(card) VALUES(FLOOR(1 + (RAND() * 20)));
INSERT INTO class(card) VALUES(FLOOR(1 + (RAND() * 20)));
INSERT INTO class(card) VALUES(FLOOR(1 + (RAND() * 20)));
INSERT INTO class(card) VALUES(FLOOR(1 + (RAND() * 20)));
INSERT INTO class(card) VALUES(FLOOR(1 + (RAND() * 20)));
INSERT INTO class(card) VALUES(FLOOR(1 + (RAND() * 20)));
INSERT INTO class(card) VALUES(FLOOR(1 + (RAND() * 20)));
INSERT INTO class(card) VALUES(FLOOR(1 + (RAND() * 20)));
INSERT INTO class(card) VALUES(FLOOR(1 + (RAND() * 20)));
INSERT INTO class(card) VALUES(FLOOR(1 + (RAND() * 20)));
INSERT INTO class(card) VALUES(FLOOR(1 + (RAND() * 20)));
INSERT INTO class(card) VALUES(FLOOR(1 + (RAND() * 20)));
INSERT INTO class(card) VALUES(FLOOR(1 + (RAND() * 20)));
INSERT INTO class(card) VALUES(FLOOR(1 + (RAND() * 20)));
INSERT INTO class(card) VALUES(FLOOR(1 + (RAND() * 20)));
INSERT INTO class(card) VALUES(FLOOR(1 + (RAND() * 20)));
INSERT INTO class(card) VALUES(FLOOR(1 + (RAND() * 20)));
INSERT INTO class(card) VALUES(FLOOR(1 + (RAND() * 20)));
 
INSERT INTO book(card) VALUES(FLOOR(1 + (RAND() * 20)));
INSERT INTO book(card) VALUES(FLOOR(1 + (RAND() * 20)));
INSERT INTO book(card) VALUES(FLOOR(1 + (RAND() * 20)));
INSERT INTO book(card) VALUES(FLOOR(1 + (RAND() * 20)));
INSERT INTO book(card) VALUES(FLOOR(1 + (RAND() * 20)));
INSERT INTO book(card) VALUES(FLOOR(1 + (RAND() * 20)));
INSERT INTO book(card) VALUES(FLOOR(1 + (RAND() * 20)));
INSERT INTO book(card) VALUES(FLOOR(1 + (RAND() * 20)));
INSERT INTO book(card) VALUES(FLOOR(1 + (RAND() * 20)));
INSERT INTO book(card) VALUES(FLOOR(1 + (RAND() * 20)));
INSERT INTO book(card) VALUES(FLOOR(1 + (RAND() * 20)));
INSERT INTO book(card) VALUES(FLOOR(1 + (RAND() * 20)));
INSERT INTO book(card) VALUES(FLOOR(1 + (RAND() * 20)));
INSERT INTO book(card) VALUES(FLOOR(1 + (RAND() * 20)));
INSERT INTO book(card) VALUES(FLOOR(1 + (RAND() * 20)));
INSERT INTO book(card) VALUES(FLOOR(1 + (RAND() * 20)));
INSERT INTO book(card) VALUES(FLOOR(1 + (RAND() * 20)));
INSERT INTO book(card) VALUES(FLOOR(1 + (RAND() * 20)));
INSERT INTO book(card) VALUES(FLOOR(1 + (RAND() * 20)));
INSERT INTO book(card) VALUES(FLOOR(1 + (RAND() * 20)));

内连接时,mysql会自动把小结果集的表选为驱动表,所以大表的字段最好加上索引。
左外连接时,左表会全表扫描,所以右边大表字段最好加上索引,右外连接同理。
我们最好保证被驱动表上的字段建立了索引。

#1.联表查询
explain select *
from class
left join book
on class.card = book.card;

在这里插入图片描述

#2.建立索引
alter table book add index idx_card(card);
#3.测试
explain select *
from class
left join book
on class.card = book.card;

在这里插入图片描述

5. 分组优化

参照排序优化。

  • 建表
drop table if exists students;
CREATE TABLE students (
  id INT PRIMARY KEY AUTO_INCREMENT COMMENT "主键id",
  sname VARCHAR (24) COMMENT '学生姓名',
  age INT COMMENT '年龄',
  score INT COMMENT '分数',
	time TIMESTAMP COMMENT '入学时间'
);
 
INSERT INTO students(sname,age,score,time) VALUES('小明',22,100,now());
INSERT INTO students(sname,age,score,time) VALUES('小红',23,80,now());
INSERT INTO students(sname,age,score,time) VALUES('小绿',24,80,now());
INSERT INTO students(sname,age,score,time) VALUES('黑',23,70,now());
-- 分组优化
alter table students add index idx_sas(sname,age,score);
#没有WHERE条件,GROUP BY age不满足最左前缀,所以是Using temporary; Using filesort
explain select count(*),sname from students GROUP BY age;

在这里插入图片描述

explain select count(*),sname 
from students 
where sname="小明" and age > 22
GROUP BY score;

在这里插入图片描述

二、慢查询日志

1. 慢查询日志简介

MySQL的慢查询日志是MySQL提供的一种日志记录,用来记录在MySQL中响应时间超过阀值的语句,具体指运行时间超过long_query_time值的SQL,则会被记录到慢查询日志中。可以由它来查看哪些SQL超出了我们最大忍耐时间值。

2. 慢查询日志使用

默认情况下,MySQL数据库没有开启慢查询日志,需要手动设置参数。
查看是否开启:show variables like ‘%slow_query_log%’;
开启日志:set global slow_query_log = 1;
设置时间:set global long_query_time = 1;
查看时间: SHOW VARIABLES LIKE ‘long_query_time%’;
查看超时的sql记录日志:Mysql的数据文件夹下
C:\ProgramData\MySQL\MySQL Server 5.7\Data\设备名称-slow.log
注意:非调优场景下,一般不建议启动该参数,慢查询日志支持将日志记录写入文件,开启慢查询日志或多或少带来一定的性能影响。

-- 慢查询日志
-- 查看是否开启:show variables like '%slow_query_log%';
show variables like '%slow_query_log%';

在这里插入图片描述

-- 开启日志:
set global slow_query_log = 1;
-- 设置时间: 
set global long_query_time = 1;
-- 查看时间: 需要重新开启一个会话才能查看刚设置的时间
SHOW VARIABLES LIKE 'long_query_time%';
  • 需要重新开启一个会话才能查看刚设置的时间,cmd新建连接创建新会话
C:\Users\Administrator>mysql -uroot -p
Enter password: ****
Welcome to the MySQL monitor.  Commands end with ; or \g.
Your MySQL connection id is 7
Server version: 5.7.37-log MySQL Community Server (GPL)

Copyright (c) 2000, 2022, Oracle and/or its affiliates.

Oracle is a registered trademark of Oracle Corporation and/or its
affiliates. Other names may be trademarks of their respective
owners.

Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.

mysql> SHOW VARIABLES LIKE 'long_query_time%';
+-----------------+----------+
| Variable_name   | Value    |
+-----------------+----------+
| long_query_time | 1.000000 |
+-----------------+----------+
1 row in set, 1 warning (0.00 sec)

mysql>
  • 查询一个有300万数据的表,没有建PAGE索引,耗时超过1s
select * from person where PAGE = 51;

在这里插入图片描述

  • 打开慢查询日志文件
    在这里插入图片描述
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值