MySQL优化
当我们在写SQL语句时,有许多优化方法可以提高查询的性能和效率。下面便列出了一些主要的优化技巧,以及对应的简单SQL语句示例:
一般人想不到的优化:
GroupBy优化
高基数维度在前,低基数维度在后。
OrderBy优化
高基数维度在前,低基数维度在后。
GroupingSets代替Groupby
GroupingSets可以减少表扫描次数,提升性能。
1. 只读取必要的列
不要获取数据库中所有的列,而仅仅获取你需要的列。这可以减少数据传输量,提高处理速度。
例子:
1 2 3 4 5 6
| # 不推荐 SELECT * FROM employees;
# 推荐 SELECT first_name, last_name FROM employees;
|
2. 使用Join代替子查询
通常,使用JOIN代替子查询(sub-query)可以改善查询性能。
例子:
1 2 3 4 5 6 7 8 9 10
| # 不推荐 SELECT a.last_name FROM employees a WHERE a.department_id IN (SELECT department_id FROM departments WHERE location_id = 1000);
# 推荐 SELECT a.last_name FROM employees a JOIN departments d ON a.department_id = d.department_id WHERE d.location_id = 1000;
|
3. 尽可能少的使用LIKE
LIKE操作符是一种开销较大的操作,所以尽量避免使用它,或者至少确保它不以通配符开始。
例子:
1 2 3 4 5
| # 不推荐 SELECT first_name FROM employees WHERE first_name LIKE '%ji%';
# 推荐 SELECT first_name FROM employees WHERE first_name LIKE 'ji%';
|
4. 合理使用LIMIT
如果仅仅需要一部分结果,比如orderby,使用LIMIT可以极大缩短查询时间。
例子:
1 2
| # 取出前十个员工数据 SELECT * FROM employees LIMIT 10;
|
5. 避免NUll值
尽可能让字段保持非NULL,因为处理NULL值需要消耗更多的CPU和I/O资源。可以设计表结构让字段默认不为NULL。
1 2 3 4 5
| # 不推荐 SELECT * FROM employees WHERE phone_number IS NULL;
# 推荐 SELECT * FROM employees WHERE phone_number = '';
|
6. 尽可能减少where中数据类型的隐式转换
如果在条件查询中数据类型存在隐式转换,那么数据库可能无法正确使用索引。
例子:
1 2 3 4 5 6
| # 在id为整数类型的情况下 # 不推荐 SELECT * FROM employees WHERE id = '123';
# 推荐 SELECT * FROM employees WHERE id = 123;
|
7. 尽量避免在where子句中对字段进行函数操作
这样会导致数据库无法使用索引,从而进行全表扫描。
例子:
1 2 3 4 5
| # 不推荐 SELECT * FROM employees WHERE YEAR(birth_date) > 1970;
# 推荐 SELECT * FROM employees WHERE birth_date > '1970-01-01';
|
8. 使用Union ALL替代Union
使用Union时,数据库会进行额外的步骤去除重复的记录,消耗性能。如果明确知道两个查询返回的记录集合没有交集,那么使用UNION ALL会更有效。
例子:
1 2 3 4 5 6 7 8 9
| # 不推荐 SELECT city FROM table_a UNION SELECT city FROM table_b;
# 推荐 SELECT city FROM table_a UNION ALL SELECT city FROM table_b;
|
9. 使用批量插入 (bulk insert)
对于大量数据的插入,尽可能使用批量插入而不是一次插入单行,这将显著提高性能。
例子:
1 2 3 4 5 6 7 8
| # 不推荐 INSERT INTO employees (first_name, last_name) VALUES ('John', 'Doe'); INSERT INTO employees (first_name, last_name) VALUES ('Jane', 'Doe');
# 推荐 INSERT INTO employees (first_name, last_name) VALUES ('John', 'Doe'), ('Jane', 'Doe');
|
10. 使用 EXISTS 关键字代替 IN 关键字
在某些数据库(如MySQL)中,当查询大数据集时,EXISTS 通常比 IN 更快。如果查询生成的结果集非常大,那么 IN 关键字会使查询变得非常低效。
例子:
1 2 3 4 5 6 7 8 9
| # 不推荐 SELECT employee_id FROM employees WHERE employee_id IN (SELECT employee_id FROM orders);
# 推荐 SELECT employee_id FROM employees e WHERE EXISTS (SELECT 1 FROM orders o WHERE o.employee_id = e.employee_id);
|
11. 使用 EXPLAIN 关键字
EXPLAIN关键字可以告诉你 DBMS 是如何处理 SQL 语句的。通过这种方式,你可能会发现你认为应该被利用的索引实际上并没有被用到,或者查询中使用了全表扫描等低效操作。
1 2
| # 分析查询的执行计划 EXPLAIN SELECT * FROM employees WHERE department_id = 10;
|
12. 尽量避免使用HAVING子句
只有当必须在聚合函数上应用条件时,才使用HAVING。HAVING子句用于过滤聚合结果,如果可能,尝试通过调整WHERE子句条件来实现相同的结果。
例子:
1 2 3 4 5 6 7 8 9 10 11 12 13
| # 不推荐 SELECT department_id, COUNT(*) FROM employees GROUP BY department_id HAVING COUNT(*) > 5;
# 推荐 SELECT department_id FROM (SELECT department_id, COUNT(*) AS cnt FROM employees GROUP BY department_id ) as t WHERE t.cnt > 5;
|
13. 避免使用笛卡尔积(CROSS JOIN)
CROSS JOIN可能会生成很多行的结果集,可能造成SQL查询运行的效率低下。
1 2 3 4 5
| # 不推荐 SELECT * FROM employees CROSS JOIN departments;
# 推荐 SELECT * FROM employees, departments WHERE employees.department_id = departments.department_id;
|
14. 使用索引
在大型数据库中,合理使用索引可以提高查询速度。需要注意,索引并不是越多越好,否则可能在插入和更新操作时拖慢速度。
例子:
1 2 3
| # 建立索引 CREATE INDEX idx_employees_last_name ON employees (last_name);
|
15. 在适当的地方使用存储过程
Stored Procedures能够提高应用的性能,通过预编译和重复使用执行计划。而且它们还可以减少网络使用,在数据库和应用之间只需要传递参数就可以了。
1 2 3
| CREATE PROCEDURE GetEmployeeCountByDepartment @DepartmentId int AS SELECT COUNT(*) FROM Employees WHERE DepartmentId = @DepartmentId;
|
16. 分解复杂查询
复杂的查询可能会要求数据库进行大量的计算。如果可以,最好将这些复杂查询分解成几个简单的查询。
例子:
1 2 3 4 5 6
| # 不推荐 SELECT a.*, b.* FROM a, b WHERE a.id = b.id AND a.id = ... ;
# 推荐 SELECT * FROM a WHERE a.id = ... ; SELECT * FROM b WHERE b.id = ... ;
|
17. 合理使用Distinct
使用Distinct关键字可以删除查询结果中的重复行,不过在进行删除操作时,优化器需要对结果进行排序,这会带来巨大的性能开销。尽量在必要的时候使用。
例子:
1 2 3 4 5
| # 不推荐 SELECT DISTINCT first_name, last_name FROM employees;
# 若可能,推荐 SELECT first_name, last_name FROM employees;
|
18. By的优化
GroupBy优化
高基数维度在前,低基数维度在后。
OrderBy优化
高基数维度在前,低基数维度在后。
GroupingSets代替Groupby
GroupingSets可以减少表扫描次数,提升性能。