
原因:MySQL 内部可能觉得第一个字段就用范围,结果集应该很大,还需要回表,回表效率不高,不如直接采用全表扫描 但是我们可以强制走索引


使用了强制走索引让联合索引第一个字段范围查找也走索引,扫描的行rows看上去也少了点,但是最终查找效率不一定比全表扫描高,因为回表效率不高
对于这种情况,如果可以使用覆盖索引,就使用覆盖索引进行优化

2.in 和 or 在表数据量比较大的情况会走索引,在表记录不多的情况下会选择全表扫描

将十万行数据的employees表复制一份插入几行数据,再进行查询

发现进行了全表扫描

3.like xx% 无论数据量多少一般情况都会走索引

MySQL 底层使用索引下推(Index Condition Pushdown,ICP) 来对 like xx%进行优化。
索引下推: 对于辅助的联合索引(idx_name_age_position),通常按照最左前缀原则,SELECT * FROM employees WHERE name like 'LiLei%' AND age = 22 AND position ='manager' 因为在 name 是范围查询,过滤完后,age 和 position 是无序的,后续索引无法使用,只会走name字段索引。
MySQL 范围查找为什么没有使用索引下推优化? 可能因为范围查找结果集一般较大,like xx%在大多数情况下,过滤后结果集较小。而结果集大的时候,每次检索出来都要匹配后面的字段,不一定比立即回表要快。但是也不是绝对的,有些时候 Like xx%也不会走索引下推。
先来看两条 SQL 语句:

我们发现第一条 SQL 进行了全表扫描,第二条 SQL 走了索引。对应第一条SQL,MySQL 通过计算执行成本发现走索引成本比全部扫描更高(走索引需要遍历 name 字段,再进行回表操作查出最终数据,比直接查聚簇索引树更慢)。对于这种情况可以使用覆盖索引进行优化。至于 MySQL 如何选择最终索引,可以用 Trace 工具进行查看。但开启trace工具会影响 MySQL 性能,用完之后需立即关闭。

下面是执行后的Trace中的内容:


分析: 案例3 查询时使用了 name 索引,age 和 postion 用于排序,不会出现 Using filesort

分析: 案例4 查询时使用了 name 索引,age 和 postion 顺序与创建索引树不一致,出现了 Using filesort


分析: 案例5 查询时使用了 name 索引,age 和 postion 顺序与创建索引树不一致,但 name、age 为常量,MySQL 会自动优化,不会出现 Using filesort

分析: 案例6 排序顺序一样,但 order by 默认升序,导致与索引的排序方式不同,出现了 Using filesort 。 MySQL8.0 以上版本有降序索引可以支持这种查询。

分析: 案例7 对于排序来说,多个相等条件也是范围查询,出现了 Using filesort 。

分析: 案例8 原因同前面的例子,可以使用覆盖索引优化。
MySQL排序总结:
1、MySQL支持两种方式的排序 filesort 和 index,Using index是指MySQL扫描索引本身完成排序。Using filesort 是指MySQL扫描聚簇索引(整张表)进行排序。index效率高,filesort效率低。
2、order by 满足两种情况会使用 Using index(不绝对)
3、尽量在索引列上完成排序,遵循最左前缀法则。
4、如果 order by 的条件不在索引列上,就会产生Using filesort。
5、能用覆盖索引尽量用覆盖索引
6、group by 与 order by 很类似,其实质是先排序后分组(group by 底层:先执行一次 order by 再进行分组),遵照索引创建顺序的最左前缀法则。对于group by的优化如果不需要排序的可以加上order by null 禁止排序。注意,where高于having,能写在where中的限定条件就不要去having限定了。
Using filesort 文件排序原理 filesort文件排序方式有:
MySQL 通过比较系统变量 max_length_for_sort_data(默认1024字节) 的大小和需要查询的字段总大小来判断使用哪种排序模式。


从执行计划可以了解的信息:
整个过程会读取 t2 表的所有数据(扫描100行),然后遍历这每行数据中字段 a 的值,根据 t2 表中 a 的值索引扫描 t1 表中的对应行(扫描100次 t1 表的索引,1次扫描可以认为最终只扫描 t1 表一行完整数据,也就是总共 t1 表也扫描了100行)。因此整个过程扫描了 200 行 。
2. 基于块的嵌套循环连接 Block Nested-Loop Join(BNL)算法 原理:把驱动表的数据读入到 join_buffer 中,然后扫描被驱动表,把被驱动表每一行取出来跟 join_buffer 中的数据做对比

整个过程对表 t1 和 t2 都做了一次全表扫描,因此扫描的总行数为10000(表 t1 的数据总量) + 100(表 t2 的数据总量) = 10100。并且 join_buffer 里的数据是无序的,因此对表 t1 中的每一行,都要做 100 次判断,所以内存中的判断次数是 100 * 10000= 100 万次(非扫描次数) 。 注意: join_buffer 的大小是由参数 join_buffer_size 控制,默认256k。如果 t2 放不下就会使用分段策略(先从 t2 表取出部分数据,比对完就清空 join_buffer,再重新拿出来余下的部分进行比对)。
被驱动表的关联字段无索引为什么要选择使用 BNL 算法而不使用 NLJ 算法? 如第二条 SQL,如果使用 NLJ 算法扫描行数为 100 * 10000 = 100万,这个是磁盘扫描。使用 BNL 算法仅需扫描 100100 行。
对于表关联 SQL 的优化
select * from t2 straight_join t1 on t2.a = t1.a; 代表指定mysql选着 t2 表作为驱动表原则:小表驱动大表
注意:根据某个字段 count 不会统计字段为 null 的行
四条语句的效率几乎可以忽略,效率对比如下: 字段有索引: count(* )≈count(1)>count(字段)>count(主键 id) 段)>count(主键 id) 字段无索引: count(*)≈count(1)>count(主键 id)>count(字段)
常见优化方法:
show table status,它是一个估值,使用该查询效率很高到此这篇关于MySQL索引优化实例分析的文章就介绍到这了,更多相关MySQL索引优化内容请搜索以前的文章或继续浏览下面的相关文章希望大家以后多多支持!