MySQL的查询优化:减少数据访问、重写SQL语句、重新设计表、添加索引


查询操作在实际开发中用的最多,程序调用的也最多。对于线上的程序,性能的主要压力便来自于查询,尤其是复杂查询。
常用的查询优化策略有:减少数据访问、重写SQL,重新设计表、添加索引

一.减少数据访问

减少数据访问一般考虑的方向是:
1.减少应用程序对数据库的访问:
数据库和应用程序之间是网络通信,每次通信都有I/O,所以应该减少通信次数。
能不通信的尽量不通信,能一次完成的尽量不要分多次。
措施:为应用程序增加缓存
2.减少实际扫描的记录数:
查询时扫描的行越多,查询越慢,尽量加以控制
措施:尽量配合条件去使用limit、offset,对比一下这两句的性能差异:

select * from xxx limit 10 offset 10000; -- 慢
select * from xxx where id > 10000 limit 10; -- 快

在这里插入图片描述
3.减少数据的获取量:
例如:只需要获取几列数据时,不要获取全部列,避免使用:

select * from ...

二.重写SQL语句

由于复杂查询严重降低了并发性,因此为了让程序更适于扩展,我们可以把复杂的查询分解为多个简单的查询。⼀般来说多个简单查询的总成本是小于⼀个复杂查询的。
对于需要进行大量数据的操作,可以分批执行,以减少对生产系统产生的影响,从而缓解复制超时。
由于MySQL连接 (JOIN) 严重降低了并发性,对于高并发高性能的服务,应该尽量避免连接太多表,如果可能,对于⼀些严重影响性能的SQL,建议程序在应用层就实现部分连接的功能。
这样的好处是: 可以更方便、更高效地缓存数据,方便迁移表到另外的机器,扩展性也更好。

1.连接语句的问题

由于连接的成本比较高,因此对于高并发的应用,应该尽量减少有连接的查询,连接的表的个数不能太多,连接的表建议控制在4个以内。
互联网应用比较常见的⼀种情况是,在数据量比较小的时候,连接的开销不大,这个时候⼀般不会有性能问题,但当数据量变大之后,连接的低效率问题就暴露出来了,成为整个系统的瓶颈所在。
所以对于数据库应用的设计,最好在早期就确定未来可能会影响性能的⼀些查询,进行反范式设计减少连接的表,或者考虑在应用层进行连接。

2.连接语句的优化

(1)ON、USING子句中的列确认有索引。如果优化器选择了连接的顺序为B、A,那么我们只需要在A表的列上创建索引即可。
例如,对于这个查询语句:

SELECT B.*,A.*FROM B JOIN A ON B.col1=A.col2;

MySQL会全表扫描 B 表,对 B 表的每⼀行记录探测 A 表的记录(利用A 表 col2 列上的索引)。
(2)最好是能转化为 INNER JOIN,LEFT JOIN 的成本比 INNER JOIN 高很多。
(3) 使用 EXPLAIN 检查连接,留意 EXPLAIN 输出的 rows 列,如果 rows 太高,比如几千、上万,那么就需要考虑是否索引不佳或连接表的顺序不当。
在这里插入图片描述

(4)反范式设计,出于性能的考虑,这样可以减少连接表的个数,加快存取数据的速度。
(5)考虑在应用层实现连接。
对于⼀些复杂的连接查询,更值得推荐的做法是将它分解为几个简单的查询,可以先执行查询以获得⼀个较小的结果集,然后再遍历此结果集,最后根据⼀定的条件去获取完整的数据,这样做往往是更高效的。
因为我们把数据分离了,更不容易发生变化,更方便缓存数据,数据也可以按照设计的需要从缓存或数据库中进行获取。
例如,对于如下的查询:

SELECT a.* from a where a.id B;

如果id=1~15的记录已经被存储在缓存(如Memcached)中了,那么我们只需要到数据库查询:

SELECT a.*from a where a.id=16;

SELECT a.* from a where a.id=17;

而且,把IN列表分解为等值查找,往往可以提高性能。
(6)⼀些应用可能需要访问不同的数据库实例,这种情况下,在应用层实现连接将是更好的选择。

3.Group、Order、Distinct 的优化

GROUP BY、DISTINCT、ORDER BY 这几类子句比较类似,其共同点就是在数据库执行的时候,会在数据库底层进行排序,GROUP BY 默认会进行ORDER BY 排序
优化方向如下:
(1)尽量对较少的行进行排序
(2)如果连接了多张表,ORDER BY 的列应该属于连接顺序的第⼀张表。
(3)利用索引排序。
(4)GROUP BY、ORDER BY语句参考的列应该尽量在⼀个表中,如果不在同⼀个表中,那么可以考虑冗余⼀些列,或者合并表。
(5)需要保证索引列和ORDER BY的列相同,且各列均按相同的方向进行排序。
在这里插入图片描述

4.优化子查询

对于数据库来说,在绝大部分情况下,连接查询会比子查询更快。
使用连接的方式,MySQL优化器⼀般可以生成更佳的执行计划,可以预先装载数据,更高效地处理查询。
而子查询往往需要运行重复的查询,子查询生成的临时表上也没有索引,因此效率会更低。
⼀些商业数据库可以智能地识别子查询,转化子查询为连接查询。但MySQL对于子查询的优化⼀直不佳,就目前的研发实践来说,子查询应尽量改写成JOIN 的写法。

5.优化limit子句

limit 子句的问题主要出在 offset 上,offset的值很大,效率就会很差。
⼀般可以进行如下调整:
(1)从应用程序上进行调整,限制页数,只显示前几页,超过了⼀定的页数后显示 “获取更多”
(2)能避免 offset,尽量避免
(3)不能避免的时候使用条件约束查询结果的范围,不要匹配太多。

6.优化in列表

in 列表不宜过长,最好不要超过200。
对于高并发的业务,小于几十为佳。
在这里插入图片描述

三.重新设计表结构

有些情况下,我们即使是重写SQL或添加索引也是解决不了问题的,这个时候可能要考虑更改表结构的设计。
比如,可以增加⼀个缓存表,暂存统计数据,或者可以增加冗余列,以减少连接。
优化的主要方向是进行反范式设计。

四.添加索引

⽣产环境中的性能问题,可能 80% 的都是索引的问题,所以优化好索引,就已经有了⼀个好的开始。
在这里插入图片描述
好的索引能起到事半功倍的效果,但是在实际操作时也要注意⼀些事情,不合理的 SQL 语句会导致索引失效。
1.在where子句中进行null值判断的话会导致引擎放弃索引而产生全表扫描

SELECT id FROM table WHERE num is not null;

在建立数据库的时候因尽量为字段设置默认值, 如int类型可以使用0, varchar类型使用’’
当你在指定类型大小如int(11)时, 其实空间就已经固定了, 即时存的是 null 也是这个大小
2.避免在 where 子句中使用 != , <> 这样的符号, 否则会导致引擎放弃索引而产生全表扫描

SELECT id FROM table WHERE num != 0;

3.避免在where子句中 = 的左边使用表达式操作或者函数操作

SELECT id FROM table WHERE num / 2 = 1; 
-- 应换成
SELECT id FROM table WHERE num = 2; 
-- 函数操作也是同理
SELECT id FROM table WHERE SUBSTRING(name, 1, 2) = 'wise'; 
-- 这种尽量避免

在这里插入图片描述

4.避免在 where 子句中使用 like 模糊查询

-- 放弃索引,全表扫描
select count(1) from `user` where nickname like '%ab'; 
-- 使⽤索引
select count(1) from `user` where nickname like 'ab%';

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

奋斗的源

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值