查询优化SQL
让SQL尽量可以命中索引,可以提示查询的效率(但是数据库如果不走索引的速度较快,就不会去走索引)
最左匹配法则
由于联合索引中包括了多个列,那么对于这多个列的匹配就有一定的规则,就是最左匹配法则,
在使用联合索引时,必须满足从左边开始匹配索引列。
假设现在存在以name,age,phone的联合索引
情况一:where name = ? and age = ? and phone = ?
where name = ? and phone = ? and age = ? ,
只要查询条件中包含了索引的全部列,并且使用and作为连接,那么就会命中全部索引(mysql对于这样的情况会自动优化sql来命中尽可能多的索引)
情况二:where name = ? and age = ? ,命中name和age两个索引
where name = ? and phone = ? , 只命中name一个索引
where age = ? and phone = ? , 没有命中索引
如果查询条件中只包含了部分索引列,并使用and作为连接,那么就会根据索引中列的顺序进行最左匹配,首先会看条件中有没有包含索引内的第一个列,依次后推,msql对于你的sql语句内的条件是会自动调优的,但是索引的匹配还是从左到右开始判断的,只要索引从左到右匹配有字段匹配不到,那么就不会命中后续的索引,不管你sql中的条件有没有包含后续的索引列,都不会继续匹配。
情况三:where name = ? and age = ? or phone = ? , 不会命中索引
不管你的条件有没有包含索引列,只有sql中出现了or,索引就会失效,可以通过把or换成 union[合并去重 + all 合并不去重]
来实现
select * from table_name where name = ? and age = ? union select * from table_name where phone = ? ,命中name和age索引
修改后实现的效果是一样的,但是可以让此sql命中部分索引
索引失效的情况
情况一: 使用了!=
进行条件判断
原因:索引的底层是B+树,是一般是按照主键或者其他索引条件,从小到大,然后从左到右存储的,因此在查询的时候支持等值查询,来快速定位,或者范围查询来筛选出符合的数据,但是不等于的操作需要排除特定字段,从而筛选出其他字段,这样即无法满足等值匹配,也无法实现范围查询,从而导致索引失效。
情况二:在条件中使用了or关键字
原因:or会使查询的条件出现不确定性,对于索引来说希望的使条件较为确定的查询,但是or的出现会增了一定的复杂度,mysql内部的优化器就会不走索引,从而取全表扫描
情况三:使用is null 或者 is not null
原因:前者是由于索引存储的数据内是不会放null值的,后者原因则与不等于基本相同
情况四:对列数据做运算,使用了函数
原因:索引中存储的数据的表内字段的原始数据,在进行匹配时也只能满足等值匹配或范围查询,经过了计算后就无法匹配到原始值导致索引失效
情况五:模糊匹配使用了左%,如name like '%aaa'
原因:查询索引时是从左到右扫描的,%代表了不定长的前缀,这样会导致索引扫描过程中,无法排除无关的记录,最后的效果和全表扫描差不多,数据库就不会查询索引从而直接全表扫描
情况六:分页查询,数据量太大,如limit 999 ,100 是查询第999页的100条记录,而分页的底层实现是,先查出全部的100000记录,然后去除前99900,再拿到后面的100条记录。
情况七:使用in来进行等值查询索引时,里面的元素个数不能超过1000,根据阿里开发规约里面描述超过1000就会使索引失效
情况八:varcher类型,匹配时字符编码不一致
Explain关键词
EXPLAIN 是 MySQL 中用于分析查询执行计划的重要工具。通过 EXPLAIN,可以了解查询的性能瓶颈,并采取相应的优化措施。
效果如下:
主要的字段:
1.type(索引类型)
- 这条sql的连接的类型,性能由好到差为NULL、system、const、eq_ref、ref、range、 index、all
- system:查询系统中的表
- const:根据主键查询
- eq_ref:主键索引查询或唯一索引查询
- ref:索引查询
- range:范围查询
- index:索引树扫描
- all:全盘扫描
2.possible_keys:可能会用到的索引
3.key:实际命中索引
4.key_len: 最长索引宽度
- 计算规则:
5.Extra 额外的优化建议,提供额外的信息,描述查询的执行细节。
- 常见值:
- Using where:使用了 WHERE 子句过滤数据。
- Using index:覆盖索引,查询只需从索引中获取数据,无需访问表。
- Using temporary:使用了临时表(如排序或分组)。
- Using filesort:需要额外的排序操作。
- Using join buffer:使用了连接缓冲区。
- Impossible WHERE:WHERE 子句始终为假,返回空结果集。
优化查询
基于上述一些规则和情况,可以优化对于的查询方式
- 查询的条件尽可能命中索引
- 使用联合索引时要遵循最左匹配法则
- 要尽可能避免索引失效的出现
- 对于分页查询的优化,比如现在每页有100个记录,我们要查询999页
- 原本的方式:select column_1, column_2 from table_name limit 999,100 会导致索引失效
- 优化的方式:已知主键为自增的就可以添加条件 select column_1, column_2 from table_name where id > 99899 limit 100 使主键索引生效
- 尽可能避免回表查询
- 回表:通过非主键索引查询到了主键,并且还需要查询非主键索引和主键以外的字段值时,就会通过主键索引再次查询数据,从而查询到对应的字段值
- 使用覆盖索引:对于需要查询的字段,添加联合索引,这样就不会就会覆盖到原本的非主键索引,也不会取查询主键索引,解决了回表问题
- 比如:目前给name创建了非主键索引
- 查询语句为select id,name,age,gender from user where name = ? ,由于创建了非主键索引name,那么就会更加此非主键索引查询,由于索引的底层,基于非主键索引中存放的数据只有主键,因此只能查到主键,要想查到其他字段的值,就需要到主键索引内查出完整的数据,出现回表
- 所以,我们可以为name,age,gender创建联合索引,这样根据name查询后,就可以直接从联合索引的索引中拿到需要的数据
- 索引下推:在匹配索引的过程中,一般是根据索引列从左到右分部查询过滤,但是所有下推,会先判断全部的查询条件是否满足索引,如果全部满足或者部分满足,则会把这些满足所有的条件,一步之间过滤,而不是根据索列一个一个匹配过滤,从而提高了查询效率,MySQL5.6之后默认会开启,
- 比如,目前有auther,price创建了联合索引
- 查询语句为:select id from books where auther = ? and price > 100 , 如果没有索引下推,首先就会根据索引从左往右匹配,所有第一次回匹配到auther这字段,满足所有的匹配规则则会进行第一次过滤,之间交给后续的过滤规则接着匹配索引来查询,如果说在数据库内auther是张三的很多,但是价格大于100的可能就几个,那么就会使效率降低
- 如果使用了索引下推,首先就会先查看全部的查询条件是否满足索引的匹配规则,满足则根据查询条件一次过滤,这样就会提高查询的效率。
- 查看Extra中的值是 Using index condition,就表示触发了索引下推