想要对一条查询语句进行优化,首先要对其进行分析,MySQL提供了这个机制, 可以通过explain sql
或者desc sql
的语法去获取MySQL对某一条语句的执行计划(MySQL优化之后的),explain
的用法这里就不再赘述了,在另外一篇文章中有详细的解读.
(想自学习编程的小伙伴请搜索圈T社区,更多行业相关资讯更有行业相关免费视频教程。完全免费哦!)
对一条sql的优化可以分为两部分,第一部分是对语句的优化,比如将子查询改写为join等,第二部分是与索引相关的优化,在这一阶段可能会修改语句以让查询尽可能的命中索引,甚至会通过修改索引来达到这个目的.
首先我们需要让查询尽可能的命中索引,通常情况下在一张表上会有各种花里胡哨的查询,我们很难让每一个查询都完美命中,因此我们假设认为我们在为bad case 做优化,不考虑对其他的查询造成的影响.
在使用联合索引的时候,要想多字段命中索引,需要遵循最左前缀原则.
假如现在表上有school_age
的联合索引,那么下面的语句是可以使用索引的:
# 根据school查询
select * from user where school = '卡塞尔'
# 根据scholl和age进行查询
select * from user where school = '卡塞尔' and age = 12
而直接通过age的查询是无法命中索引的,select * from school where age = 10
,这一点可以通过explain来证实.
在查询语句中,如果返回的字段较少,那么应该尽量的使需要的所有字段包含在索引中,这样可以使用覆盖索引来加快查询速度.
查询语句优化的第一步,首先从大的层面上分析一下语句,得到以下问题的答案:
是否请求了不需要的数据?
这个最常出现的是请求了过多的列,也就是select *
,此外还有查询了10000行但是在拿到前10行之后就扔掉了其他的数据.
是否扫描了过多的数据?
在理想的情况下,我们希望能到做到扫描的数据行数和需要返回的数据行数是一样的,但是在实际操作中比较困难,但是我们首先应该检查这一项,已确定当前扫描的行数是必要的.
当一个语句太过于复杂的时候,我们总是难以掌握它的性能,因此我们可以将一个复杂的查询拆分成多个查询,然后在应用程序中进行关联.
子查询想必关联查询,性能一般是较差的,因此可以将子查询改为关联表查询.
count()函数需要扫面大量的数据,在MyISAM中速度是比较快的,但是在其他存储引擎却不是,对count()语句可以有以下的优化策略.
确定是否真的需要数量
曾经见过一个count(*)的语句,但是对结果的使用仅仅是判断结果是否大于0,这时语句可以大大的减少扫面的数量来达到相同的作用:
select 1 from user where age = 100 limit 1.
这样仅需要扫描一行数据就可以达到相同的作用.
使用近似值
当表中数据量非常大的时候,很多的count查询是不需要精确计数的,此时可以使用其他近似值,比如explain中的行数,比如information_schema.tables中的行数等.
添加汇总表
如果需要经常的进行count,那么我们应该额外添加一张表或者一列来记录这个数值,而不是每次进行查询.
优化关联查询
优化limit语句
当limit offset,limit
中的offset值很大时,查询的性能会直线下降
MySQL提供了一些用于我们"提示"MySQL服务器应该怎样进行这个查询,需要注意的是,使用hint很有可能不会给你的程序带来性能上的提升,反而可能是性能下降,因此在使用前请确保自己了解该hint的作用.这里列举一些常用的hint的作用.
该提示让mysql不对这条数据的结果进行缓存.SELECT SQL_NO_CACHE xxx, yyy FROM TABLE;
告诉mysql这条语句的结果需要缓存.SELECT SQL_CALHE * FROM TABLE;
告诉MySQL这条数据的优先级很高,在竞争一些互斥的资源时,这条语句将最先获得资源,SELECT HIGH_PRIORITY * FROM TABLE;
与上面一个相反.
该hint会在insert和replace的时候使用,可以是的MySQL服务器立即返回结果,但是插入操作则在表空闲的时候进行.insert delayed into .....
该hint告诉MySQL按照语句中的顺序进行多个表的关联操作,不要进行"优化".select STRAIGHT_JOIN * from table1 join table2.
该hint告诉mysql,将查询结果放入到临时表中,然后尽快释放表锁.SELECT SQL_BUFFER_RESULT * FROM TABLE ...;
这两个hint只可以在select语句使用,它告诉MySQL结果集很大/很小.因此MySQL可以使用内存/文件进行排序等操作.SELECT SQL_BIG_RESULT * FROM TABLE ...;
FORCE INDEX和IGNORE INDEX
这两个hint告诉MySQL此查询语句强制使用或者不使用哪个索引.SELECT * FROM TABLE FORCE INDEX (FIELD) ...;
完。