Mysql进阶优化篇01——四万字详解数据库性能分析工具(深入全面详细,收藏备用)

Posted 半旧518

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Mysql进阶优化篇01——四万字详解数据库性能分析工具(深入全面详细,收藏备用)相关的知识,希望对你有一定的参考价值。

前 言
🍉 作者简介:半旧518,长跑型选手,立志坚持写10年博客,专注于java后端
🍌 专栏简介:mysql进阶,主要讲解mysql数据库进阶知识,包括索引、数据库调优、分库分表等
🌰 文章简介:本文将介绍数据库优化的步骤、思路、性能分析工具,比如慢查询、EXPLAIN,SHOW PROFILING等,并且对各个工具执行性能分析结果性能参数都有详细的介绍解释、建议收藏备用。
🍓 相关推荐:

目录

1.数据库服务器的优化步骤

数据库的优化整个流程划分成了 观察(Show status) 和 行动(Action) 两个部分。数据库的优化可以总结为下图。字母 S 的部分代表观察(会使用相应的分析工具),字母 A 代表的部分是行动(对应分析可以采取的行动)。

可以从图中看到,在整个流程中需要用到很多分析工具:比如慢查询,EXPLAIN,SHOW PROFILING等,这篇文章就会介绍这些数据库性能分析工具。
简单小结如下:

可以看到数据库调优的步骤中越往金字塔尖走,其成本越高,效果越差,因此我们在数据库调优的过程中,要重点把握金字塔底部的sql及索引调优数据库表结构调优系统配置参数调优等软件层面的调优。

2.查看系统性能参数

可以使用SHOW STATUS语句查询一些数据库服务器的性能参数使用频率
其语法如下:

SHOW [GLOBAL][SESSION] STATUES LIKE '参数';

一些常用的性能参数如下:

Connections:连接MySQL服务器的次数。
Uptime:MySQL服务器的上线时间。
Slow_queries:慢查询的次数。
Innodb_rows_read:Select查询返回的行数
Innodb_rows_inserted:执行INSERT操作插入的行数
Innodb_rows_updated:执行UPDATE操作更新的行数
Innodb_rows_deleted:执行DELETE操作删除的行数
Com_select:查询操作的次数。
Com_insert:插入操作的次数。对于批量插入的 INSERT 操作,只累加一次。
Com_update:更新操作的次数。
Com_delete:删除操作的次数。

举几个例子,玩一把。查看mysql的上线时间

mysql> show status like 'connections';
+---------------+-------+
| Variable_name | Value |
+---------------+-------+
| Connections   | 9     |
+---------------+-------+
1 row in set (0.01 sec)

看看存储引擎增删改查的行数。

mysql> show status like  'innodb_rows_%';
+----------------------+-------+
| Variable_name        | Value |
+----------------------+-------+
| Innodb_rows_deleted  | 0     |
| Innodb_rows_inserted | 0     |
| Innodb_rows_read     | 8     |
| Innodb_rows_updated  | 0     |
+----------------------+-------+
4 rows in set (0.00 sec)

3.统计SQL的查询成本:last_query_cost

先来造一下数据(友情提醒:上一篇文章已经造过,如果您是从上一篇文章跟着阅读过来的,不用重新造了哟。)

CREATE DATABASE atguigudb1;

USE atguigudb1;

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 //


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 //

# 存储过程1:创建插入课程表存储过程
DELIMITER //
CREATE PROCEDURE insert_course( max_num INT )
BEGIN 
	DECLARE i INT DEFAULT 0; 
	SET autocommit = 0;   #设置手动提交事务
	REPEAT  #循环
	SET i = i + 1;  #赋值
	INSERT INTO course (course_id, course_name ) VALUES
	(rand_num(10000,10100),rand_string(6)); 
	UNTIL i = max_num 
	END REPEAT; 
	COMMIT;  #提交事务
END //
DELIMITER ;

# 存储过程2:创建插入学生表存储过程
CREATE PROCEDURE insert_stu( max_num INT )
BEGIN 
	DECLARE i INT DEFAULT 0; 
	SET autocommit = 0;   #设置手动提交事务
	REPEAT  #循环
	SET i = i + 1;  #赋值
	INSERT INTO student_info (course_id, class_id ,student_id ,NAME ) VALUES
(rand_num(10000,10100),rand_num(10000,10200),rand_num(1,200000),rand_string(6)); 
	UNTIL i = max_num 
	END REPEAT; 
	COMMIT;  #提交事务
END //

# 插入课程数据
CALL insert_course(100);
#插入学生数据
CALL insert_stu(1000000);

执行查询操作并且查看sql执行成本,Value表示I/O加载的数据页的页数。

mysql> select * from student_info where id = 900001;
+--------+------------+--------+-----------+----------+---------------------+
| id     | student_id | name   | course_id | class_id | create_time         |
+--------+------------+--------+-----------+----------+---------------------+
| 900001 |     128284 | jbCKPX |     10080 |    10001 | 2022-05-31 11:01:54 |
+--------+------------+--------+-----------+----------+---------------------+
1 row in set (0.00 sec)

mysql> show status like 'last_query_cost';
+-----------------+----------+
| Variable_name   | Value    |
+-----------------+----------+
| Last_query_cost | 1.000000 |
+-----------------+----------+
1 row in set (0.00 sec)

再来个大的。

mysql>  select * from student_info where id between 900001 and 900100;
+--------+------------+--------+-----------+----------+---------------------+
| id     | student_id | name   | course_id | class_id | create_time         |
+--------+------------+--------+-----------+----------+---------------------+
| 900001 |     128284 | jbCKPX |     10080 |    10001 | 2022-05-31 11:01:54 |
// ...
| 900099 |      45120 | MZOSay |     10081 |    10026 | 2022-05-31 11:01:54 |
| 900100 |      83397 | lQyTXg |     10034 |    10058 | 2022-05-31 11:01:54 |
+--------+------------+--------+-----------+----------+---------------------+
100 rows in set (0.00 sec)

mysql> show status like 'last_query_cost';
+-----------------+-----------+
| Variable_name   | Value     |
+-----------------+-----------+
| Last_query_cost | 41.136003 |
+-----------------+-----------+
1 row in set (0.00 sec)

不知道大家有没有发现,上面的查询页的数量是刚才的 41 倍,但是查询的效率并没有明显的变化,实际上这两个 SQL 查询的时间基本上一样,查询last_query_cost对于比较开销是非常有用的,特别是我们有好几种查询方式可选的时候。

🎈 SQL查询是一个动态的过程,从页加载的角度,我们可以得到以下两点结论:
1.位置决定效率:数据库缓冲池>内存>磁盘。
2.批量决定效率:顺序读取>大于随机读取,有时候批量顺序读取多个页甚至会比随机加载一个页更快。
在实际生产中,我们可以利用这个特点,把经常用于查询的数据尽量放在缓冲池中,其次我们可以充分利用磁盘的吞吐能力,批量读取数据。

4.定位执行慢的 SQL:慢查询日志

慢查询日志用来记录相应时间超过阈值的语句,它可以帮助我们发现那些执行时间特别长的sql语句,以期进行针对性优化。一般mysql的慢查询日志默认关闭,非调优情况不建议开启,避免影响数据库的性能。

4.1 开启慢查询日志

1️⃣开启slow_query_log

查看

mysql> show variables like '%slow_query_log%';
+---------------------+------------------------------------------------------+
| Variable_name       | Value                                                |
+---------------------+------------------------------------------------------+
| slow_query_log      | OFF                                                  |
| slow_query_log_file | D:\\mysql-5.7.26-winx64\\data\\DESKTOP-1PB99O1-slow.log |
+---------------------+------------------------------------------------------+
2 rows in set, 1 warning (0.00 sec)

修改,注意这里要加global,因为它是全局系统变量,否则会报错哟。

mysql> set global slow_query_log='ON';
Query OK, 0 rows affected (0.02 sec)

再查看。

mysql> show variables like '%slow_query_log%';
+---------------------+------------------------------------------------------+
| Variable_name       | Value                                                |
+---------------------+------------------------------------------------------+
| slow_query_log      | ON                                                   |
| slow_query_log_file | D:\\mysql-5.7.26-winx64\\data\\DESKTOP-1PB99O1-slow.log |
+---------------------+------------------------------------------------------+
2 rows in set, 1 warning (0.00 sec)

2️⃣修改long_query_time阈值

查看。

mysql> show variables like '%long_query_time%';
+-----------------+-----------+
| Variable_name   | Value     |
+-----------------+-----------+
| long_query_time | 10.000000 |
+-----------------+-----------+
1 row in set, 1 warning (0.02 sec)

修改。

mysql> set global long_query_time = 1;
Query OK, 0 rows affected (0.00 sec)

再查看。

mysql> show global variables like '%long_query_time%';
+-----------------+----------+
| Variable_name   | Value    |
+-----------------+----------+
| long_query_time | 1.000000 |
+-----------------+----------+
1 row in set, 1 warning (0.00 sec)

🔊记得要加global,否则默认只在当前会话,不过,即使加global上面的修改还都只是临时的修改,当数据库服务器重启以后,以上修改就会失效。要想永久的生效,需要更改my.cnf文件,然后重启数据库服务器。

slow_query_log=ON
slow_query_log_file=/var/lib/mysql/atguigu-slow.log
long_query_time=3
log_output=FILE

4.2 案例演示

1️⃣ 建表

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`)
) ENGINE=INNODB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;

2️⃣ 设置参数 log_bin_trust_function_creators

(第3节已经完成)

创建函数,假如报错

This function has none of DETERMINISTIC......

命令开启:允许创建函数设置:

set global log_bin_trust_function_creators=1;   # 不加global只是当前窗口有效。

3️⃣创建函数

(第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 ;
#测试
SELECT rand_string(10);

产生随机数值(第3节已经完成):

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 ;
#测试:
SELECT rand_num(10,100);

4️⃣创建存储过程

DELIMITER //
CREATE PROCEDURE insert_stu1(  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(10,100),rand_num(10,1000)); 
UNTIL i = max_num 
END REPEAT; 
COMMIT;  #提交事务
END //
DELIMITER ;

步骤5:调用存储过程

#调用刚刚写好的函数, 4000000条记录,从100001号开始
mysql> CALL insert_stu1(100001,4000000);
Query OK, 0 rows affected (10 min 47.03 sec)

注意,这个时间会比较长,请耐心等待几分钟哟。结束后可以查询下是不是插入成功了。

select count(*) from student;

📘 有一个小细节在这里提下,就是上面查询数据量的语句在存储引擎使用MyISAM时会比使用InnoDB时快很多,这是因为MyISAM存储引擎会有字段专门表示记录数。

接下来执行一下下面的查询操作,制造慢查询的场景。

mysql> set  long_query_time = 1;
Query OK, 0 rows affected (0.00 sec)

mysql> SELECT * FROM student WHERE stuno = 3455655;
+---------+---------+--------+------+---------+
| id      | stuno   | name   | age  | classId |
+---------+---------+--------+------+---------+
| 3355654 | 3455655 | QQFFkl |   57 |     904 |
+---------+---------+--------+------+---------+
1 row in set (3.47 sec)

mysql> select * from student where name = 'QQFFkl';
+---------+---------+--------+------+---------+
| id      | stuno   | name   | age  | classId |
+---------+---------+--------+------+---------+
|  143213 |  243214 | qQffkL |   95 |     543 |
|  225733 |  325734 | qQffkL |   10 |     861 |
|  280275 |  380276 | QqfFKL |   50 |     118 |
| 1355465 | 1455466 | QqfFKL |   52 |     195 |
| 1676763 | 1776764 | qQffkL |   11 |     906 |
| 1766208 | 1866209 | qqFfKl |   11 |     396 |
| 1870789 | 1970790 | qqFfKl |   97 |     182 |
| 2368740 | 2468741 | QQFFkl |   51 |     645 |
| 2386799 | 2486800 | qQffkL |   11 |     875 |
| 3170932 | 3270933 | QqfFKL |   50 |      92 |
| 3355654 | 3455655 | QQFFkl |   57 |     904 |
| 3966226 | 4066227 | qQffkL |   96 |     629 |
+---------+---------+--------+------+---------+

查看下慢查询的记录。

mysql> show status like 'slow_queries';
+---------------+-------+
| Variable_name | Value |
+---------------+-------+
| Slow_queries  | 2     |
+---------------+-------+
1 row in set (0.00 sec)

🎯补充:在Mysql中,还有另外一个变量min_examined_row_limit用来控制慢查询日志,他的含义是,在查询时,查询时间超过long_query_time 的日志,还要保证查询扫描过的记录数满足min_examined_row_limit才会被记录到慢查询日志。一般它默认是0,我们也一般不会去修改它。

SHOW VARIABLES like 'min%'
OK
时间: 0.002s

以上是关于Mysql进阶优化篇01——四万字详解数据库性能分析工具(深入全面详细,收藏备用)的主要内容,如果未能解决你的问题,请参考以下文章

DBA整理的万字详解MySQL性能优化,值得收藏!

❤️爆肝四万字的MySQL总结全面整理+详细解释❤️

最强最全面的Hive SQL开发指南,超四万字全面解析

最强最全面的Hive SQL开发指南,超四万字全面解析

最强最全面的Hive SQL开发指南,超四万字全面解析

万字宝典 |《 Spark性能优化全书》推荐收藏!