Day08-11. MySQL-索引-结构_哔哩哔哩_bilibili
使用 EXPLAIN
在查询语句前加上 EXPLAIN 关键字:
EXPLAIN SELECT * FROM orders WHERE customer_id = 123 AND order_date > '2023-01-01';
执行上述 EXPLAIN 语句,查看输出结果。MySQL 会返回一个包含查询执行计划的表格(如下)。
具体解释:
EXPLAIN 输出表格包含多个列,每列提供不同的查询计划信息。常见列包括:
1、id:查询的标识符,表示查询的执行顺序。
2、select_type:查询类型,如 SIMPLE(简单查询),PRIMARY(主查询),UNION(联合查询的一部分),SUBQUERY(子查询)。
3、table:查询涉及的表。
4、type:连接类型,表示MySQL如何查找行。常见类型按效率从高到低排列为:
-
system:表只有一行(常见于系统表)。
-
const:表最多有一个匹配行(索引为主键或唯一索引)。
-
eq_ref:对于每个来自前一个表的行,表中最多有一个匹配行。
-
ref:对于每个来自前一个表的行,表中可能有多个匹配行。
-
range:使用索引查找给定范围的行。
-
index:全表扫描索引。
-
ALL:全表扫描。
5、possible_keys:查询中可能使用的索引。
6、key:实际使用的索引。
7、key_len:使用的索引键长度。
8、ref:使用的列或常量,与索引比较。
9、rows:MySQL 估计的要读取的行数。
10、filtered:经过表条件过滤后的行百分比。
11、Extra:额外的信息,如 Using index(覆盖索引),Using where(使用 WHERE 子句过滤),Using filesort(文件排序),Using temporary(使用临时表)。
优化查询路径
根据 EXPLAIN 输出,采取以下措施优化查询路径:
确保使用索引
如果 type 列显示为 ALL 或 index,说明表进行了全表扫描。可以通过创建适当的索引来优化查询。例如:
CREATE INDEX idx_customer_date ON orders (customer_id, order_date);
优化查询条件
避免在索引列上使用函数或进行计算。改写查询条件以利用索引。例如:
-- 避免
SELECT * FROM orders WHERE YEAR(order_date) = 2023;
-- 改为
SELECT * FROM orders WHERE order_date BETWEEN '2023-01-01' AND '2023-12-31';
使用覆盖索引
如果查询只涉及索引中的列,可以避免回表,提高性能。例如:
CREATE INDEX idx_covering ON orders (customer_id, order_date, order_id);
-- 查询只涉及索引中的列
SELECT customer_id, order_date, order_id FROM orders WHERE customer_id = 123;
分解复杂查询
将复杂查询分解为多个简单查询,可以提高性能。例如:
-- 复杂查询
SELECT * FROM orders o JOIN customers c ON o.customer_id = c.id WHERE c.name = 'John Doe';
-- 分解为两个简单查询
SELECT id FROM customers WHERE name = 'John Doe';
-- 假设查询结果为 123
SELECT * FROM orders WHERE customer_id = 123;
实际示例
假设有一个 employees 表和一个 departments 表:
CREATE TABLE employees (
id INT AUTO_INCREMENT PRIMARY KEY,
first_name VARCHAR(50),
last_name VARCHAR(50),
department_id INT,
hire_date DATE,
INDEX (department_id),
INDEX (hire_date)
);
CREATE TABLE departments (
id INT AUTO_INCREMENT PRIMARY KEY,
name VARCHAR(50)
);
查询所有在某个日期后加入某部门的员工:
EXPLAIN
SELECT e.id, e.first_name, e.last_name
FROM employees e
JOIN departments d ON e.department_id = d.id
WHERE d.name = 'Sales' AND e.hire_date > '2023-01-01';
示例 EXPLAIN 输出:
+----+-------------+-------+--------+---------------+---------+---------+-------------------+------+--------------------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+-------+--------+---------------+---------+---------+-------------------+------+--------------------------+
| 1 | SIMPLE | d | const | PRIMARY | PRIMARY | 4 | const | 1 | |
| 1 | SIMPLE | e | ref | department_id | department_id | 4 | const | 10 | Using where |
+----+-------------+-------+--------+---------------+---------+---------+-------------------+------+--------------------------+
从 EXPLAIN 输出可以看出:
-
d 表使用了 PRIMARY 索引,类型为 const,表示是一个常量查找。
-
e 表使用了 department_id 索引,类型为 ref,表示引用查找。
进一步优化:
-
如果查询频繁,可以为 departments.name 创建索引。
-
确保 hire_date 上有索引。
优化后的索引创建:
CREATE INDEX idx_department_name ON departments (name);
再次执行 EXPLAIN:
EXPLAIN
SELECT e.id, e.first_name, e.last_name
FROM employees e
JOIN departments d ON e.department_id = d.id
WHERE d.name = 'Sales' AND e.hire_date > '2023-01-01';
优化后的输出可能显示更好的执行计划,减少查询时间。