明天你会感谢今天奋力拼搏的你。
ヾ(o◕∀◕)ノヾ
写在文章之前:其实在架构技术选型时就应该去考虑数据是应该放到MongoDB、ES、MQ、Redis还是放到数据库中,确定数据的存放的地方才好进行相关优化。
MySQL调优最重要的一个就是查询优化,查询优化主要就是对慢查询的优化,而慢查询的原因一般都是由于查询数据量过大引起的,因此对慢查询的优化更多的就是去减少数据的访问量。
如何确认慢查询的原因,减少数据的访问量?
我们首先需要对慢查询进行分析。
mysql有一个参数:记录所有执行超过long_query_time(默认10s)设定的SQL语句日志。相关参数如下,仅供参考:
# 慢查询日志
show variables like 'slow_query_log';
# 开启慢查询
set GLOBAL slow_query_log = 1;
# 关闭慢查询
set GLOBAL slow_query_log = 0;
# 慢查询默认阈值
show variables like 'long_query_time';
# 设置慢查询阈值为0,方便测试
set GLOBAL long_query_time = 0;
# 慢查询日志记录的路径(Windows默认是在C:\ProgramData\MySQL\MySQL Server XXXX\Data下)
show variables like 'slow_query_log_file';
# 查询没有应用索引的SQL是否记录到慢查询日志中
show variables like 'log_queries_not_using_indexes';
# 开启没有应用索引的SQL是否记录到慢查询日志中
set GLOBAL log_queries_not_using_indexes = 1;
注意:上面的参数修改不是永久生效,重启MySQL后失效。要永久生效需要在MySQL的配置文件中设置参数值。
慢查询日志内容介绍:
如下图所示,一段慢查询内容一般是以# Time开头,查询语句SELECT XXXX结尾。
Time表示执行的时间;Query_time表示语句执行时间,Lock_time表示获取锁的时间,一段SQL在数据库中执行的时间可以理解为上面2个时间相加。
Rows_sent表示查询的结果数据,如下图所示为18条,Rows_examined表示扫描了多少条数据,如下图是256条。
然后可以通过mysqldumpslow命令,对慢查询的日志结果进行指定的排序以进行分析。很简单这里就不进行细讲了,可以查看网上其他文章。
在SQL查询的前面加上EXPLAIN关键字,该命令会返回一个结果集,包含查询执行的详细信息。
返回列说明
注意:EXPLAIN展示的是MySQL查询优化器优化后的执行计划,而非原始的SQL结构。
MySQL 查询优化器是数据库的核心组件之一,负责将 SQL 语句转换为最高效的执行计划。
以下列举一些优化器可能执行的关键优化策略:
1、子查询优化:MySQL会将 IN/NOT IN 子查询转换为 JOIN,减少嵌套查询。还会将多个子查询合并为单个查询。
-- 原始SQL
SELECT * FROM orders WHERE customer_id IN (SELECT id FROM customers WHERE country = 'China');
-- 优化后
SELECT orders.* FROM orders JOIN customers ON orders.customer_id = customers.id WHERE customers.country = 'China';
2、表达式简化:如:WHERE a = 5 + 3 会简化为 WHERE a =8。WHERE 1 = 1 AND a > 5 会简化为 WHERE a > 5
3、连接顺序优化:调整多表JOIN的顺序,优先处理数据量小的表,减少中间结果集。
1、选择最有效的索引
SELECT * FROM users WHERE age > 30 AND gender = 'F';
-- 若存在复合索引 (age, gender) 和 (gender, age),优化器会选择更高效的索引。
2、索引条件下推
EXPLAIN SELECT * FROM products WHERE category = 'book' AND price > 100;
-- 若有复合索引 (category, price),会先在索引中过滤price条件。
3、覆盖索引
SELECT id, name FROM users WHERE age = 25;
-- 若索引包含 (age, id, name),则无需回表。
1、利用索引顺序避免额外排序
SELECT * FROM orders ORDER BY create_time;
-- 若有索引 (create_time),则直接按索引顺序读取。
2、利用索引实现分组
SELECT category, COUNT(*) FROM products GROUP BY category;
-- 若有索引 (category),则按索引分组更高效。
1、在子查询或派生表中提前计算聚合结果
SELECT (SELECT COUNT(*) FROM users) AS total_users, ...;
-- 优化器可能会缓存子查询结果。
2、优化MIN/MAX:若字段有索引,直接取索引首尾值。
SELECT MIN(price) FROM products;
-- 若有索引 (price),直接取索引第一个值。
1、优化器会尽量避免创建临时表
SELECT DISTINCT category FROM products;
-- 若category有索引,直接利用索引去重,无需临时表。
2、将临时表存储在内存中(使用tmp_table_size参数控制)
SELECT * FROM users WHERE age IN (20, 25, 30);
-- 小结果集可能使用内存临时表。
1、将 WHERE 条件尽早应用于数据源,减少中间数据量。
2、对大表查询启用多线程并行执行(MySQL 8.0+,parallel_execution_enabled参数要启动)
3、优化 INSERT/UPDATE 语句,把多次单挑插入改为批量插入。
索引调优可以查看我的另一篇文章《Mysql导致B+Tree索引失效的场景汇总》
全部评论