康师傅YYDS
索引失效案例
索引最好是全值匹配。
where条件中等值比,同时where的条件一起创建联合索引。
最佳左前缀
如果有一个联合索引,要想使用到,需要从联合索引的最左边的字段开始写,一点一点的写上。
主键插入顺序
在InnoDB中,使用自增的主键。避免索引页面分裂
计算、函数、类型转换(自动、手动)
函数:
1 CREATE INDEX idx_student_name ON student(name); 3 // type = range 4 EXPLAIN SELECT SQL_NO_CACHE * FROM student WHERE student.name LIKE 'abc%'; 6 // type = ALL 7 EXPLAIN SELECT SQL_NO_CACHE * FROM student WHERE LEFT(student.name,3) = 'abc';
计算:
1 CREATE INDEX idx_student_stuno ON student(stuno); 2 // type = ALL 3 EXPLAIN SELECT SQL_NO_CACHE id, stuno, NAME FROM student WHERE stuno+1 = 900001;
类型转换:
1 // type = ALL 2 EXPLAIN SELECT SQL_NO_CACHE * FROM student WHERE name=123; 3 // type = ref 4 EXPLAIN SELECT SQL_NO_CACHE * FROM student WHERE name='123';
范围条件右边的列索引失效
几个列联合索引,前面的查范围里,后面的就用不到了。在开发中,创建联合索引时,将常常用于范围查找的如:金额、日期等放在联合索引的最后面
不等于 != <>用不上索引
IS NULL 可以用索引 IS NOT NULL不能用索引
最好在设计数据表的时候,将字段设置为not null,比如int字段,设置默认值为0,字符串设置默认为空字符串。
not like 、like以%开头 也不能使用索引
拓展:Alibaba《Java开发手册》 【强制】页面搜索严禁左模糊或者全模糊,如果需要请走搜索引擎来解决。OR 前后两个条件存在非索引的列,索引失效
数据库和表的字符集统一使用utf8mb4
统一使用utf8mb4( 5.5.3版本以上支持)兼容性更好,统一字符集可以避免由于字符集转换产生的乱码。 不同的 字符集 进行比较前需要进行 转换 会造成索引失效。
关联查询优化
左外连接
全连接是合并、左外连接和右外连接可以互相转换
LEFT JOIN 条件用于确定如何从右表搜索行,左边一定都有(从左边取一条数据,然后就去右边表找这条数据), 所以 右边是我们的关键点,一定需要建立索引 。
内连接
内连接需要两张表公共的,没有那张表全都要的情况,两张表的地位相同,优化器会选择一个作为驱动表。
如果两张表连接条件中,只有一个表中这个字段有索引,那么这个有索引的会被作为被驱动表。
两个表的连接都存在索引的情况下,会选择数据量小的作为驱动表。(小表驱动大表)
小结
保证被驱动表的JOIN字段已经创建了索引 需要JOIN 的字段,数据类型保持绝对一致。 LEFT JOIN 时,选择小表作为驱动表, 大表作为被驱动表 。减少外层循环的次数。 INNER JOIN 时,MySQL会自动将 小结果集的表选为驱动表 。选择相信MySQL优化策略。 能够直接多表关联的尽量直接关联,不用子查询。(减少查询的趟数) 不建议使用子查询,建议将子查询SQL拆开结合程序多次查询,或使用 JOIN 来代替子查询。 衍生表建不了索引
子查询优化
子查询效率不高
原因: ① 执行子查询时,MySQL需要为内层查询语句的查询结果 建立一个临时表 ,然后外层查询语句从临时表中查询记录。查询完毕后,再 撤销这些临时表 。这样会消耗过多的CPU和IO资源,产生大量的慢查询。 ② 子查询的结果集存储的临时表,不论是内存临时表还是磁盘临时表都 不会存在索引 ,所以查询性能会受到一定的影响。 ③ 对于返回结果集比较大的子查询,其对查询性能的影响也就越大。 在MySQL中,可以使用连接(JOIN)查询来替代子查询。连接查询 不需要建立临时表 ,其 速度比子查询要快 ,如果查询中使用索引的话,性能就会更好。 结论:尽量不要使用NOT IN 或者 NOT EXISTS,用LEFT JOIN xxx ON xx WHERE xx IS NULL替代 。排序优化
优化建议: 1. SQL 中,可以在 WHERE 子句和 ORDER BY 子句中使用索引,目的是在 WHERE 子句中 避免全表扫 描 ,在 ORDER BY 子句 避免使用 FileSort 排序 。当然,某些情况下全表扫描,或者 FileSort 排 序不一定比索引慢。但总的来说,我们还是要避免,以提高查询效率。 2. 尽量使用 Index 完成 ORDER BY 排序。如果 WHERE 和 ORDER BY 后面是相同的列就使用单索引列; 如果不同就使用联合索引。 3. 无法使用 Index 时,需要对 FileSort 方式进行调优 3.1 尝试提高 sort_buffer_size 3.2 尝试提高 max_length_for_sort_data 3.3 Order by 时select * 是一个大忌。最好只Query需要的字段。INDEX a_b_c(a,b,c) order by 能使用索引最左前缀 - ORDER BY a - ORDER BY a,b - ORDER BY a,b,c - ORDER BY a DESC,b DESC,c DESC 如果WHERE使用索引的最左前缀定义为常量,则order by 能使用索引 - WHERE a = const ORDER BY b,c - WHERE a = const AND b = const ORDER BY c - WHERE a = const ORDER BY b,c - WHERE a = const AND b > const ORDER BY b,c 不能使用索引进行排序结论:type 是 ALL,即最坏的情况。Extra 里还出现了 Using filesort,也是最坏的情况。优化是必须的。 order的时候不使用limit,有可能有索引但是优化器不走,因为优化器发现数据挺多,走了二级索引之后又要回表操作,不如直接内存中排序。
- ORDER BY b,c,a - ORDER BY a ASC,b DESC,c DESC /* 排序不一致 */ - WHERE g = const ORDER BY b,c /*丢失a索引*/ - WHERE a = const ORDER BY c /*丢失b索引*/ - WHERE a = const ORDER BY a,d /*d不是索引的一部分*/ - WHERE a in (...) ORDER BY b,c /*对于排序来说,多个相等条件也是范围查询*/
分组、分页优化
group by
group by 使用索引的原则几乎跟order by一致 ,group by 即使没有过滤条件用到索引,也可以直接使用索引。 group by 先排序再分组,遵照索引建的最佳左前缀法则当无法使用索引列,增大 max_length_for_sort_data 和 sort_buffer_size 参数的设置 where效率高于having,能写在where限定的条件就不要写在having中了 减少使用order by,和业务沟通能不排序就不排序,或将排序放到程序端去做。 Order by、group by、distinct这些语句较为耗费CPU,数据库的CPU资源是极其宝贵的。 包含了order by、group by、distinct这些查询的语句,where条件过滤出来的结果集请保持在1000行以内,否则SQL会很慢。limit
一般分页查询时,通过创建覆盖索引能够比较好的提高性能。头疼的是limit 2000000,10 此时需要排序出前2000010条记录,然后仅仅返回2000000-2000010的记录,其他记录丢弃了,查询排序的代价非常大。
1、在索引上完成排序分页操作,最后根据主键关联回原表查询所需要的其他列内容。1 EXPLAIN SELECT * FROM student t,(SELECT id FROM student ORDER BY id LIMIT 2000000,10) a 2 WHERE t.id = a.id;
2、该方案适用于主键自增的表,可以把Limit 查询转换成某个位置的查询 。
1 EXPLAIN SELECT * FROM student WHERE id > 2000000 LIMIT 10;
覆盖索引
概念
理解方式一:索引是高效找到行的一个方法,但是一般数据库也能使用索引找到一个列的数据,因此它不必读取整个行。毕竟索引叶子节点存储了它们索引的数据; 当能通过读取索引就可以得到想要的数据,那就不需要读取行了。一个索引包含了满足查询结果的数据就叫做覆盖索引。 理解方式二:非聚簇复合索引的一种形式,它包括在查询里的SELECT、JOIN和WHERE子句用到的所有列(即建索引的字段正好是覆盖查询条件中所涉及的字段)。 简单说就是, 索引列+主键 包含 SELECT 到 FROM之间查询的列 。覆盖索引的利弊
好处: 1. 避免Innodb表进行索引的二次查询(回表) 2. 可以把随机IO变成顺序IO加快查询效率 弊端: 索引字段的维护 总是有代价的。因此,在建立冗余索引来支持覆盖索引时就需要权衡考虑了。这是业务 DBA,或者称为业务数据架构师的工作。 给字符串创建前缀索引; 使用前缀索引,定义好长度,就可以做到既节省空间,又不用额外增加太多的查询成本。 区分度 前面已经讲过区分度,区分度越高越好。因为区分度越高,意味着重复的键值越少。alter table teacher add index index1(email); #或 alter table teacher add index index2(email(6));使用前缀索引就用不上覆盖索引对查询性能的优化了,这也是你在选择是否使用前缀索引时需要考虑的一个因素。
索引(条件)下推
Index Condition Pushdown(ICP)是MySQL 5.6中新特性,是一种在存储引擎层使用索引过滤数据的一种优化方式。ICP可以减少存储引擎访问基表的次数以及MySQL服务器访问存储引擎的次数。 ICP的使用条件: ① 只能用于二级索引(secondary index) ②explain显示的执行计划中type值(join 类型)为 range 、 ref 、 eq_ref 或者 ref_or_null 。 ③ 并非全部where条件都可以用ICP筛选,如果where条件的字段不在索引列中,还是要读取整表的记录到server端做where过滤。 ④ ICP可以用于MyISAM和InnnoDB存储引擎 ⑤ MySQL 5.6版本的不支持分区表的ICP功能,5.7版本的开始支持。 ⑥ 当SQL使用覆盖索引时,不支持ICP优化方法。SELECT * FROM s1 WHERE key1 > 'z' AND key1 like'%a'