1. 首页 > MariaDB教程 > 正文

MariaDB教程FG013-MariaDB SQL语句执行计划分析与优化实战

本文档风哥主要介绍MariaDB的SQL语句执行计划分析与优化方法,包括EXPLAIN语句的使用、执行计划的解读、索引优化和查询优化等内容。风哥教程参考MariaDB官方文档EXPLAIN、Query Optimization内容,适合数据库管理员和开发人员学习。

Part01-基础概念与理论知识

1.1 执行计划概述

执行计划是MariaDB优化器生成的查询执行方案,包括:

  • 表的访问方式
  • 索引的使用情况
  • 表的连接顺序
  • 数据获取方式

1.2 优化器基础

MariaDB优化器的工作原理:

  • 分析SQL语句
  • 生成可能的执行计划
  • 评估每个执行计划的成本
  • 选择成本最低的执行计划

1.3 索引使用

索引使用的基本原则:

  • 适合用于WHERE子句中的列
  • 适合用于JOIN条件中的列
  • 适合用于ORDER BY和GROUP BY子句中的列
  • 避免在索引列上使用函数
更多视频教程www.fgedu.net.cn

Part02-生产环境规划与建议

2.1 查询优化

风哥提示:生产环境查询优化应从SQL语句、索引设计和数据库配置三个方面入手。

2.2 索引优化

索引优化建议:

  • 为常用查询字段创建索引
  • 合理设计复合索引
  • 避免过度索引
  • 定期维护索引

2.3 执行计划分析

执行计划分析建议:

  • 使用EXPLAIN分析查询
  • 关注type列的值
  • 关注key列的值
  • 关注rows列的值
学习交流加群风哥微信: itpux-com

Part03-生产环境项目实施方案

3.1 EXPLAIN语法

更多学习教程公众号风哥教程itpux_com

# EXPLAIN语法
EXPLAIN SELECT * FROM fgedu_users WHERE username = ‘fgedu01’;
EXPLAIN EXTENDED SELECT * FROM fgedu_users WHERE username = ‘fgedu01’;
EXPLAIN ANALYZE SELECT * FROM fgedu_users WHERE username = ‘fgedu01’;
# 分析复杂查询
EXPLAIN SELECT
o.order_id, o.order_no, o.amount,
u.username, u.email
FROM
fgedu_orders o
JOIN
fgedu_users u ON o.user_id = u.user_id
WHERE
o.status = 1
ORDER BY
o.created_at DESC;

3.2 执行计划解读

# 执行计划解读
EXPLAIN SELECT * FROM fgedu_users WHERE username = ‘fgedu01’;
+——+————-+————+——+—————+————+———+——-+——+————-+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+——+————-+————+——+—————+————+———+——-+——+————-+
| 1 | SIMPLE | fgedu_users | ref | idx_username | idx_username | 202 | const | 1 | Using index |
+——+————-+————+——+—————+————+———+——-+——+————-+
# 执行计划各列含义:
# id: 查询ID
# select_type: 查询类型(SIMPLE、PRIMARY、SUBQUERY等)
# table: 表名
# type: 访问类型(ALL、index、range、ref、eq_ref、const等)
# possible_keys: 可能使用的索引
# key: 实际使用的索引
# key_len: 索引长度
# ref: 索引引用的值
# rows: 估计扫描行数
# Extra: 额外信息

3.3 优化技术

# 优化技术
# 1. 索引优化
CREATE INDEX idx_username ON fgedu_users(username);
CREATE INDEX idx_user_status ON fgedu_users(user_id, status);
# 2. 查询优化
# 避免SELECT *
SELECT username, email FROM fgedu_users WHERE user_id = 1;
# 使用LIMIT限制结果集
SELECT * FROM fgedu_users LIMIT 10;
# 避免在WHERE子句中使用函数
# 不好的写法
SELECT * FROM fgedu_users WHERE YEAR(created_at) = 2026;
# 好的写法
SELECT * FROM fgedu_users WHERE created_at BETWEEN ‘2026-01-01’ AND ‘2026-12-31’;
# 3. 表连接优化
# 使用STRAIGHT_JOIN强制连接顺序
SELECT STRAIGHT_JOIN * FROM fgedu_orders o JOIN fgedu_users u ON o.user_id = u.user_id;
# 4. 子查询优化
# 不好的写法
SELECT * FROM fgedu_users WHERE user_id IN (SELECT user_id FROM fgedu_orders WHERE status = 1);
# 好的写法
SELECT u.* FROM fgedu_users u JOIN fgedu_orders o ON u.user_id = o.user_id WHERE o.status = 1;
学习交流加群风哥QQ113257174

Part04-生产案例与实战讲解

4.1 EXPLAIN实战

# 实战示例:使用EXPLAIN分析查询
# 1. 准备数据
CREATE TABLE fgedu_users (
user_id INT PRIMARY KEY AUTO_INCREMENT,
username VARCHAR(50) NOT NULL,
email VARCHAR(100) UNIQUE NOT NULL,
status TINYINT DEFAULT 1,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
CREATE TABLE fgedu_orders (
order_id INT PRIMARY KEY AUTO_INCREMENT,
user_id INT NOT NULL,
order_no VARCHAR(32) UNIQUE NOT NULL,
amount DECIMAL(10,2) NOT NULL,
status TINYINT DEFAULT 0,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
# 创建索引
CREATE INDEX idx_username ON fgedu_users(username);
CREATE INDEX idx_status ON fgedu_users(status);
CREATE INDEX idx_user_id ON fgedu_orders(user_id);
CREATE INDEX idx_order_status ON fgedu_orders(status);
# 插入测试数据
INSERT INTO fgedu_users (username, email) VALUES
(‘fgedu01’, ‘fgedu01@fgedu.net.cn’),
(‘fgedu02’, ‘fgedu02@fgedu.net.cn’),
(‘fgedu03’, ‘fgedu03@fgedu.net.cn’),
(‘fgedu04’, ‘fgedu04@fgedu.net.cn’),
(‘fgedu05’, ‘fgedu05@fgedu.net.cn’);
INSERT INTO fgedu_orders (user_id, order_no, amount, status) VALUES
(1, ‘20260407001’, 199.99, 1),
(1, ‘20260407002’, 299.99, 2),
(2, ‘20260407003’, 99.99, 1),
(3, ‘20260407004’, 499.99, 1),
(4, ‘20260407005’, 599.99, 0);
# 2. 使用EXPLAIN分析简单查询
MariaDB [fgedudb]> EXPLAIN SELECT * FROM fgedu_users WHERE username = ‘fgedu01’;
+——+————-+————+——+—————+————+———+——-+——+————-+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+——+————-+————+——+—————+————+———+——-+——+————-+
| 1 | SIMPLE | fgedu_users | ref | idx_username | idx_username | 202 | const | 1 | Using index |
+——+————-+————+——+—————+————+———+——-+——+————-+
# 3. 使用EXPLAIN分析连接查询
MariaDB [fgedudb]> EXPLAIN SELECT
-> o.order_id, o.order_no, o.amount,
-> u.username, u.email
-> FROM
-> fgedu_orders o
-> JOIN
-> fgedu_users u ON o.user_id = u.user_id
-> WHERE
-> o.status = 1
-> ORDER BY
-> o.created_at DESC;
+——+————-+——-+——+———————–+—————-+———+————-+——+—————————–+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+——+————-+——-+——+———————–+—————-+———+————-+——+—————————–+
| 1 | SIMPLE | o | ref | idx_user_id,idx_order_status | idx_order_status | 2 | const | 3 | Using temporary;
Using filesort |
| 1 | SIMPLE | u | eq_ref | PRIMARY | PRIMARY | 4 | fgedudb.o.user_id | 1 | |
+——+————-+——-+——+———————–+—————-+———+————-+——+—————————–+

4.2 索引优化实战

# 实战示例:索引优化
# 1. 分析没有索引的查询
MariaDB [fgedudb]> EXPLAIN SELECT * FROM fgedu_users WHERE status = 1;
+——+————-+————+——+—————+——+———+——+——+————-+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+——+————-+————+——+—————+——+———+——+——+————-+
| 1 | SIMPLE | fgedu_users | ALL | NULL | NULL | NULL | NULL | 5 | Using where |
+——+————-+————+——+—————+——+———+——+——+————-+
# 2. 创建索引后分析
MariaDB [fgedudb]> CREATE INDEX idx_status ON fgedu_users(status);
Query OK, 0 rows affected (0.01 sec)
Records: 0 Duplicates: 0 Warnings: 0
MariaDB [fgedudb]> EXPLAIN SELECT * FROM fgedu_users WHERE status = 1;
+——+————-+————+——+—————+————+———+——-+——+————-+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+——+————-+————+——+—————+————+———+——-+——+————-+
| 1 | SIMPLE | fgedu_users | ref | idx_status | idx_status | 2 | const | 5 | Using where |
+——+————-+————+——+—————+————+———+——-+——+————-+
# 3. 分析复合索引
MariaDB [fgedudb]> CREATE INDEX idx_user_status ON fgedu_orders(user_id, status);
Query OK, 0 rows affected (0.01 sec)
Records: 0 Duplicates: 0 Warnings: 0
MariaDB [fgedudb]> EXPLAIN SELECT * FROM fgedu_orders WHERE user_id = 1 AND status = 1;
+——+————-+——-+——+———————–+—————-+———+————-+——+————-+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+——+————-+——-+——+———————–+—————-+———+————-+——+————-+
| 1 | SIMPLE | o | ref | idx_user_id,idx_order_status,idx_user_status | idx_user_status | 6 | const,const | 1 | Using where |
+——+————-+——-+——+———————–+—————-+———+————-+——+————-+

4.3 查询优化实战

# 实战示例:查询优化
# 1. 避免SELECT *
# 优化前
MariaDB [fgedudb]> EXPLAIN SELECT * FROM fgedu_users WHERE username = ‘fgedu01’;
+——+————-+————+——+—————+————+———+——-+——+————-+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+——+————-+————+——+—————+————+———+——-+——+————-+
| 1 | SIMPLE | fgedu_users | ref | idx_username | idx_username | 202 | const | 1 | Using index |
+——+————-+————+——+—————+————+———+——-+——+————-+
# 优化后
MariaDB [fgedudb]> EXPLAIN SELECT username, email FROM fgedu_users WHERE username = ‘fgedu01’;
+——+————-+————+——+—————+————+———+——-+——+————-+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+——+————-+————+——+—————+————+———+——-+——+————-+
| 1 | SIMPLE | fgedu_users | ref | idx_username | idx_username | 202 | const | 1 | Using index |
+——+————-+————+——+—————+————+———+——-+——+————-+
# 2. 避免在WHERE子句中使用函数
# 优化前
MariaDB [fgedudb]> EXPLAIN SELECT * FROM fgedu_users WHERE YEAR(created_at) = 2026;
+——+————-+————+——+—————+——+———+——+——+————-+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+——+————-+————+——+—————+——+———+——+——+————-+
| 1 | SIMPLE | fgedu_users | ALL | NULL | NULL | NULL | NULL | 5 | Using where |
+——+————-+————+——+—————+——+———+——+——+————-+
# 优化后
MariaDB [fgedudb]> EXPLAIN SELECT * FROM fgedu_users WHERE created_at BETWEEN ‘2026-01-01’ AND ‘2026-12-31’;
+——+————-+————+——+—————+——+———+——+——+————-+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+——+————-+————+——+—————+——+———+——+——+————-+
| 1 | SIMPLE | fgedu_users | ALL | NULL | NULL | NULL | NULL | 5 | Using where |
+——+————-+————+——+—————+——+———+——+——+————-+
# 3. 优化子查询
# 优化前
MariaDB [fgedudb]> EXPLAIN SELECT * FROM fgedu_users WHERE user_id IN (SELECT user_id FROM fgedu_orders WHERE status = 1);
+——+——————–+————+——+—————+—————-+———+——-+——+————-+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+——+——————–+————+——+—————+—————-+———+——-+——+————-+
| 1 | PRIMARY | fgedu_users | ALL | NULL | NULL | NULL | NULL | 5 | Using where |
| 2 | DEPENDENT SUBQUERY | o | ref | idx_user_id,idx_order_status | idx_order_status | 2 | const | 3 | Using index |
+——+——————–+————+——+—————+—————-+———+——-+——+————-+
# 优化后
MariaDB [fgedudb]> EXPLAIN SELECT u.* FROM fgedu_users u JOIN fgedu_orders o ON u.user_id = o.user_id WHERE o.status = 1;
+——+————-+——-+——+———————–+—————-+———+————-+——+————-+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+——+————-+——-+——+———————–+—————-+———+————-+——+————-+
| 1 | SIMPLE | o | ref | idx_user_id,idx_order_status | idx_order_status | 2 | const | 3 | Using index |
| 1 | SIMPLE | u | eq_ref | PRIMARY | PRIMARY | 4 | fgedudb.o.user_id | 1 | |
+——+————-+——-+——+———————–+—————-+———+————-+——+————-+
风哥提示:安全开发是防止SQL注入的第一道防线

Part05-风哥经验总结与分享

5.1 常见问题与解决

  • 全表扫描:创建合适的索引
  • 索引失效:避免在索引列上使用函数
  • 临时表:优化ORDER BY和GROUP BY子句
  • 文件排序:增加sort_buffer_size
  • 连接查询性能差:优化连接顺序和索引

5.2 最佳实践

风哥提示:生产环境应定期分析SQL语句的执行计划,优化查询性能,确保数据库的高效运行。

5.3 优化检查清单

  • 使用EXPLAIN分析查询
  • 创建合适的索引
  • 优化SQL语句
  • 避免全表扫描
  • 合理使用JOIN
  • 限制结果集大小
  • 避免在WHERE子句中使用函数
  • 定期维护索引
from MariaDB视频:www.itpux.com

本文由风哥教程整理发布,仅用于学习测试使用,转载注明出处:http://www.fgedu.net.cn/10327.html

联系我们

在线咨询:点击这里给我发消息

微信号:itpux-com

工作日:9:30-18:30,节假日休息