目录
准备测试数据
创建存储过程,生成 10w 条数据。
-- 创建employees表
CREATE TABLE employees (
id INT AUTO_INCREMENT PRIMARY KEY,
name VARCHAR(50),
salary DECIMAL(10, 2)
);
-- 创建存储过程
CREATE PROCEDURE generate_test_data()
BEGIN
DECLARE i INT DEFAULT 0;
DECLARE name_value VARCHAR(50);
DECLARE salary_value DECIMAL(10, 2);
WHILE i < 600000 DO
SET name_value = CONCAT('姓名', i); -- 假设每个名字都是"姓名"加上一个数字
SET salary_value = FLOOR(RAND() * 10000); -- 假设薪水在0到10000之间
INSERT INTO employees (name, salary) VALUES (name_value, salary_value);
SET i = i + 1;
END WHILE;
END;
-- 调用存储过程,生成60w条数据
CALL generate_test_data();
SQL 执行频率
MySQL客户端连接成功后,通过show [sessionlglobal status
命令可以提供服务器状态信息。通过如下指令,可以查看当前数据库的 INSERT、UPDATE、DELETE、SELECT的访问频次:
SHOW GLOBAL STATUS LIKE 'Com_';
// output 表示当前数据库的操作频次
|Variable_name|Value|
|-------------+-----+
|Com_binlog |0 |
|Com_commit |25 |
|Com_delete |17 |
|Com_import |0 |
|Com_insert |22 |
|Com_repair |0 |
|Com_revoke |0 |
|Com_select |581 |
|Com_signal |0 |
|Com_update |21 |
|Com_xa_end |0 |
慢查询日志
慢查询日志记录了所有执行时间超过指定参数(long_query_time,单位:秒,默认10秒)的所有SQL语句的日志。通过分析慢查询日志,可以找到数据库性能的瓶颈,并进行优化。
MySQL的慢查询日志默认没有开启,需要在MySQL的配置文件(/etc/my.cnf)中配置如下信息:
启用慢查询日志
- 查看是否开启慢查询
SHOW VARIABLES LIKE 'slow_query_log';
|Variable_name |Value|
|--------------+-----+
|slow_query_log|ON |
- 修改配置文件
my.cnf(Linux)
或my.ini(Window)
:
slow_query_log = 1
slow_query_log_file = D:/MYSQL/logger/slow-query.log
long_query_time = 2 # 设置慢查询的时间阈值为2秒
- 重启 MYSQL。
查看慢查询日志
直接查看日志文件:
cat /path/to/your/slow-query.log
// 日志格式
C:\Program Files\MySQL\MySQL Server 8.0\bin\mysqld.exe, Version: 8.0.38 (MySQL Community Server - GPL). started with:
TCP Port: 3306, Named Pipe: MySQL
Time Id Command Argument
停用慢查询日志
在配置文件中设置:
slow_query_log = 0
show profiles
慢查询记录的是执行耗时时间超过预设值 2 秒的查询记录,假设现在需要优化 1 秒以内的 SQL 查询,如何定位这条记录呢?
show profiles
能够在做 SOL 优化时帮助我们了解时间都耗费到哪一条具体的 SQL 语句,从而快速定位。
查看是否开启 profiles
select @@profiling;
-- Output
|@@profiling|
|-----------+
| 0|
// 或者
show variables like 'profiling';
-- Output
Variable_name|Value|
-------------+-----+
profiling |OFF |
profiling = 0
代表关闭,我们需要把 profiling 打开,即设置为 1:
set profiling=1;
-- Output
|Variable_name|Value|
|-------------+-----+
|profiling |ON |
多次执行的 SQL 查询
SELECT e.* FROM employees e;
SELECT COUNT(*) FROM employees e;
查看 profiles
查看当前会话所产生的所有 profiles
:
show profiles; # 显示最近的几次查询
-- Output
+----------+------------+-----------------------------------+
| Query_ID | Duration | Query |
+----------+------------+-----------------------------------+
| 1 | 0.01894200 | SELECT COUNT(*) FROM employees e |
| 2 | 0.02037825 | SELECT COUNT(*) FROM employees e |
| 3 | 0.35443900 | SELECT e.* FROM employees e |
+----------+------------+-----------------------------------+
查看具体 SQL 的执行时间
用户可以根据 QUERY ID
查询执行 SQL 语句各个阶段的耗时。
show profile for query 3;
-- outpout
+--------------------------------+----------+
| Status | Duration |
+--------------------------------+----------+
| starting | 0.000070 |
| Executing hook on transaction | 0.000002 |
| starting | 0.000023 |
| checking permissions | 0.000010 |
| Opening tables | 0.000031 |
| init | 0.000003 |
| System lock | 0.000007 |
| optimizing | 0.000008 |
| statistics | 0.000012 |
| preparing | 0.000014 |
| executing | 0.354195 | -- 执行操作耗时最久
| end | 0.000016 |
| query end | 0.000004 |
| waiting for handler commit | 0.000011 |
| closing tables | 0.000010 |
| freeing items | 0.000018 |
| cleaning up | 0.000009 |
+--------------------------------+----------+
此外,还可以查询更丰富的内容:
show profile cpu,block io for query 3;
-- Output
+--------------------------------+----------+----------+------------+--------------+---------------+
| Status | Duration | CPU_user | CPU_system | Block_ops_in | Block_ops_out |
+--------------------------------+----------+----------+------------+--------------+---------------+
| starting | 0.000070 | 0.000000 | 0.000000 | NULL | NULL |
| Executing hook on transaction | 0.000002 | 0.000000 | 0.000000 | NULL | NULL |
| starting | 0.000023 | 0.000000 | 0.000000 | NULL | NULL |
| checking permissions | 0.000010 | 0.000000 | 0.000000 | NULL | NULL |
| Opening tables | 0.000031 | 0.000000 | 0.000000 | NULL | NULL |
| init | 0.000003 | 0.000000 | 0.000000 | NULL | NULL |
| System lock | 0.000007 | 0.000000 | 0.000000 | NULL | NULL |
| optimizing | 0.000008 | 0.000000 | 0.000000 | NULL | NULL |
| statistics | 0.000012 | 0.000000 | 0.000000 | NULL | NULL |
| preparing | 0.000014 | 0.000000 | 0.000000 | NULL | NULL |
| executing | 0.354195 | 0.328125 | 0.031250 | NULL | NULL |
| end | 0.000016 | 0.000000 | 0.000000 | NULL | NULL |
| query end | 0.000004 | 0.000000 | 0.000000 | NULL | NULL |
| waiting for handler commit | 0.000011 | 0.000000 | 0.000000 | NULL | NULL |
| closing tables | 0.000010 | 0.000000 | 0.000000 | NULL | NULL |
| freeing items | 0.000018 | 0.000000 | 0.000000 | NULL | NULL |
| cleaning up | 0.000009 | 0.000000 | 0.000000 | NULL | NULL |
+--------------------------------+----------+----------+------------+--------------+---------------+
explain
上述四种方法均以执行时间作为衡量SQL性能的标准,但这种方法仅能提供大致的评估,并非精确衡量SQL语句性能的手段。为了深入理解SQL语句的执行效率,我们应利用 EXPLAIN
命令来分析其执行计划。
EXPLAIN
是 MySQL 中的一项关键功能,它用于剖析 SQL 查询的执行过程。通过 EXPLAIN
,我们可以获得 MySQL 执行SELECT
语句的详细步骤,这有助于我们洞察数据库引擎对查询的优化策略和处理细节。
基本查询
单表查询
mysql> EXPLAIN SELECT * FROM employees ;
-- Output
+----+-------------+-----------+------------+------+---------------+------+---------+------+--------+----------+-------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-----------+------------+------+---------------+------+---------+------+--------+----------+-------+
| 1 | SIMPLE | employees | NULL | ALL | NULL | NULL | NULL | NULL | 550116 | 100.00 | NULL |
+----+-------------+-----------+------------+------+---------------+------+---------+------+--------+----------+-------+
1 row in set, 1 warning (0.00 sec)
多表查询
EXPLAIN
SELECT e.* FROM employees e
LEFT JOIN test_table tt
ON e.id = tt.id;
-- Output
+----+-------------+-------+------------+--------+---------------+---------+---------+-----------+--------+----------+-------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-------+------------+--------+---------------+---------+---------+-----------+--------+----------+-------------+
| 1 | SIMPLE | e | NULL | ALL | NULL | NULL | NULL | NULL | 550116 | 100.00 | NULL |
| 1 | SIMPLE | tt | NULL | eq_ref | PRIMARY | PRIMARY | 4 | test.e.id | 1 | 100.00 | Using index |
+----+-------------+-------+------------+--------+---------------+---------+---------+-----------+--------+----------+-------------+
2 rows in set, 1 warning (0.00 sec)
参数说明
以下是一些主要的EXPLAIN
输出参数及其含义:
-
id:查询中每个表的顺序编号,标识查询执行的顺序。
- 如果 id 相同,从上往下,顺序执行。
- id 值越大,执行优先级越高,越先被执行。
-
select_type:表示查询类型,例如
SIMPLE
(简单查询)、PRIMARY
(主查询)、SUBQUERY
(子查询)、DERIVED
(派生表,从FROM子句中临时创建的表)等。 -
table:当前正在访问的表名。
-
type:访问类型,反映了MySQL在表中找到所需行的方式,
- 如
ALL
(全表扫描)、INDEX
(对整个索引进行扫描)、RANGE
(范围扫描)、EQ_REF
(唯一性索引扫描)、REF
(非唯一性索引扫描)、CONST
(对于主键或唯一索引)等。 - 性能由好到差:
Null > system > const > eq_ref > ref > range > index > all
。
- 如
-
possible_keys:该查询可能用到的索引列表。
-
key:实际使用的索引,如果没有选择任何索引,则显示NULL。
-
key_len:使用的索引长度,可以帮助推算出 MySQL 实际检索了多少个索引项。
-
ref:显示与索引相对应的列或者常量,比如关联操作中的列引用。
-
rows:MySQL根据统计信息估算的需要读取的数据行数,影响性能的一个重要因素。
-
filtered:表示存储引擎返回的数据在服务器层过滤后,预计能剩下多少行的比例。
-
Extra:提供额外信息。
Using index
表示使用覆盖索引Using where
表示在存储引擎层应用WHERE条件过滤Using temporary
表示使用了临时表Using filesort
表示MySQL需要进行外部排序等。