MySQL # 优化你的SQL语句

Posted LRcoding

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了MySQL # 优化你的SQL语句相关的知识,希望对你有一定的参考价值。

1. 查询SQL尽量不使用 select *,而是 select 具体字段

正确使用

SELECT id, name FROM student;

反例: SELECT * FROM student;

  • 只取需要的字段,节省资源
  • select * 进行查询时,很可能不会使用到覆盖索引,造成回表查询

2. 如果知道查询结果只有一条,或者只要最大/最小一条,建议用 limit 1

正确使用

SELECT id, name FROM student WHERE name = '张三' limit 1;

反例: SELECT id, name FROM student WHERE name = ‘张三’;

  • 加上 limit 1 之后,只要找到了对应的一条记录,就不会继续向下扫描,提高了效率
  • 如果 name 为唯一索引的话,可以不加 limit 1

3. 尽量避免在 where 子句中使用 or 来连接条件,使用 union all

正确使用

# 查询 id 为 1,或者 age 为 18 岁的用户
SELECT id, name, age FROM student WHERE id = 1
UNION ALL
SELECT id, name, age FROM student WHERE age = 18;

反例:SELECT id, name, age FROM student WHERE id = 1 OR age = 18;

  • 使用 or 可能会使索引失效,从而全表扫描

4. 优化 limit 分页,使用 order by + 索引

正确使用

SELECT id, name FROM student ORDER BY id LIMIT 10000, 10;

反例:SELECT id, name FROM student LIMIT 10000, 10;

  • 当偏移量很大的时候,查询效率就会很低。

    因为mysql并非是跳过偏移量直接去取后面的数据,而是先把偏移量 加上 要取的条数,然后再把前面偏移量这一段的数据抛弃掉再返回。

5. 优化 like 语句,尽量不要 前模糊

正确使用

SELECT id, name FROM student WHERE name like '张%';

反例:SELECT id, name FROM student WHERE name like ‘%张’;

  • 将 % 放到前面,并不走索引

6. 使用 where 条件限定要查询的数据,避免返回多余的行

正确使用

# 查询某个学生是否为VIP
SELECT id, name FROM student WHERE id = '1' and isVip = '1';

反例:SELECT id, name FROM student WHERE isVip = ‘1’; 再判断查出的结果中,id 是否包含 1

  • 需要什么数据,就查什么数据,避免返回多余的数据

7. 尽量避免在索引上使用 mysql 的内置函数

正确使用

# 查询最近七天内登陆过的用户(假设loginTime加了索引)
SELECT id, loginTime FROM student WHERE loginTime >= DATE_ADD(NOW(), INTERVAL -7 DAY)

反例:SELECT id, loginTime FROM student WHERE DATE_ADD(loginTime, INTERVAL 7 DAY) >= now();

  • 索引列上使用 MySQL 的内置函数,索引失效

8. 尽量避免在 where 子句中对字段进行表达式操作

正确使用

SELECT id, name FROM student WHERE age = 18;

反例:SELECT id, name FROM student WHERE age - 1 = 10;

  • 对加了索引的字段进行运算,会让索引失效,进行全表扫描

9. inner join、left join、right join,优先使用inner join,如果使用left join,左边表结果集尽量小

  • inner join:内连接,在两张表进行连接查询时,只保留两张表中完全匹配的结果集
  • left join:在两张表进行连接查询时,会返回左表所有的行,即使在右表中没有匹配的记录
  • right join:在两张表进行连接查询时,会返回右表所有的行,即使在左表中没有匹配的记录

正确使用

SELECT s.* FROM (SELECT * FROM student WHERE id > 2) s
LEFT JOIN teacher t ON s.id = t.teachId;

反例:SELECT s.* FROM student s LEFT JOIN teacher t ON s.id = t.teachId WHERE s.id > 2;

10. 尽量避免在 where 子句中使用 != 或 <> 操作符

正确使用

SELECT id, name FROM student WHERE age > 18;
SELECT id, name FROM student WHERE age < 18;

反例:SELECT id, name FROM student WHERE age <> 18;

  • 使用 != 和 <> 很可能会让索引失效

11. 使用联合索引时,注意索引列的顺序,一般遵循最左匹配原则

表结构:(有一个联合索引 idx_userid_age,userId在前,age在后)

CREATE TABLE `user`(
  `id` int (11) NOT NULL AUTO_INCREMENT,
  `userId` int(11) NOT NULL,
  `age` int (11) DEFAULT NULL,
  `name` varchar (255) NOT NULL,
  PRIMARY KEY (`id`),
  KEY `idx_userid_age` (`userId`,`age`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8;

正确使用

SELECT id, name FROM student WHERE userId = 1 and age = 18;

反例:SELECT id, name FROM student WHERE age = 18;

  • 当我们创建了一个 联合索引 的时候,如(k1, k2, k3),相当于创建了(k1)、(k1、k2)和(k1、k2、k3)三个索引,这就是最左匹配原则
  • 联合索引不满足最左原则,索引一般会失效,但是这个还跟Mysql优化器有关的

12. 对查询进行优化,应考虑在 where 及 order by涉及的列上建立索引,尽量避免全表扫描

正确使用

# 添加索引
ALTER TABLE student ADD INDEX idx_address_age ( address, age );

SELECT id, name FROM student WHERE address = '山东' ORDER BY age;

反例:SELECT id, name FROM student WHERE address = ‘山东’ ORDER BY age; 直接使用

13. 如果插入数据过多,使用批量插入

<foreach>

14. 在适当的时候,使用覆盖索引

覆盖索引能够使 SQL语句 不需要回表,仅仅访问索引就能够得到所有需要的数据

15. 慎用 distinct 关键字

带 distinct 的语句cpu时间和占用时间都高于不带distinct的语句。

因为当查询很多字段时,如果使用distinct,数据库引擎就会对数据进行比较,过滤掉重复数据,然而这个比较、过滤的过程会占用系统资源,cpu时间。

16. 删除冗余和重复索引

比如一个索引为 KEY idx_userId (userId) ,另一个为 KEY idx_userid_age (userId, age),那么就可以删除单独的 userId 索引

17. 如果数据量较大,优化修改、删除语句

可以分批次操作,如果一次性操作太多数据,可能会有lock wait timeout exceed的错误,所以建议分批操作。

18. where 子句中考虑使用 默认值 代替null

设计表时,可以给字段设置默认值(例如,设置 age的默认值为 0)

正确使用

SELECT * FROM student WHERE age > 0;

反例:SELECT * FROM student WHERE age IS NOT NULL;

19. 不要有超过 5 个以上的表连接

如果一定需要连接很多表才能得到数据,那么意味着糟糕的设计了。

20. exist & in的合理使用

正确使用

# 查询某企业所有部门的所有员工
SELECT * FROM employee e EXISTS (SELECT 1 FROM department  d WHERE e.deptId = d.id);

反例:SELECT * FROM employee WHERE deptId IN (SELECT id FROM department);

  • exists查询先执行主查询,获得数据后(一次连接),再放到子查询中做条件验证(一次连接),根据验证结果(true或者false),来决定主查询的数据结果是否保留。
  • 如果使用 in :需要先查询部门表(一次连接),再由得到的部门id 查询员工表(N次连接)

21. 尽量用 union all 替代 union

如果检索结果中不会有重复的记录,推荐union all 替换 union。

  • 如果使用 union,不管检索结果有没有重复,都会尝试进行合并,然后在输出最终结果前,进行排序

22. 索引不宜太多,一般 5 个以内

索引并不是越多越好,索引虽然提高了查询的效率,但是也降低了插入和更新的效率

23. 尽量使用数字型字段,若只含数值信息的字段尽量不要设计为字符型

相对于数字型字段,字符型会降低查询和连接的性能,并会增加存储开销。

24. 索引不适合建在有大量重复数据的字段上,如性别

因为SQL优化器是根据表中数据量来进行查询优化的,如果索引列有大量重复数据,Mysql查询优化器推算发现不走索引的成本更低,很可能就放弃索引了。

25. 尽量避免向客户端返回过多数据量

正确使用

# 查询最近一年注册的学生信息
SELECT * FROM student WHERE create_time >= DATE_SUB(NOW(), INTERVAL 1 Y) LIMIT 0, 200;
# 例如先显示200条,往后翻的时候再重新获取

反例:SELECT * FROM student WHERE create_time >= DATE_SUB(NOW(), INTERVAL 1 Y);

26. 当在 SQL语句中连接多个表时,使用表的别名

27. 尽可能使用 varchar / nvarchar 代替 char / nchar

28. 为了提高 group by 语句的效率,可以在执行到该语句前,使用 having 把不需要的记录过滤掉。

正确使用

SELECT sex, AVG(age) FROM student 
GROUP BY sex HAVING sex = '男';

反例:SELECT sex, AVG(age) FROM student GROUP BY sex;

29. 如果字符类型是字符串,where 时一定要用 引号 括起来,否则索引失效

正确使用

SELECT id, name FROM student WHERE phone = '12345';

反例:SELECT id, name FROM student WHERE phone = 12345;

  • 因为不加单引号时,是字符串跟数字的比较,它们类型不匹配,MySQL会做隐式的类型转换,把它们转换为浮点数再做比较。

以上是关于MySQL # 优化你的SQL语句的主要内容,如果未能解决你的问题,请参考以下文章

mysql优化和sql语句优化总结

MySQL # 优化你的SQL语句

mysql优化方法陈列

MySQL性能优化

mysql之性能优化

mysql explain用法