首页 > 数据库 >MySQL的InnoDB索引失效的场景和优化

MySQL的InnoDB索引失效的场景和优化

时间:2023-10-16 14:26:23浏览次数:56  
标签:使用 查询 索引 InnoDB MySQL WHERE ORDER SELECT

康师傅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
不能使用索引进行排序
- 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 /*对于排序来说,多个相等条件也是范围查询*/
结论:type 是 ALL,即最坏的情况。Extra 里还出现了 Using filesort,也是最坏的情况。优化是必须的。 order的时候不使用limit,有可能有索引但是优化器不走,因为优化器发现数据挺多,走了二级索引之后又要回表操作,不如直接内存中排序。

分组、分页优化

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'

 

其他策略

 
change buffer的使用场景
1. 普通索引和唯一索引应该怎么选择?其实,这两类索引在查询能力上是没差别的,主要考虑的是对 更新性能 的影响。所以,建议你 尽量选择普通索引 。 2. 在实际使用中会发现, 普通索引 和 change buffer 的配合使用,对于 数据量大 的表的更新优化还是很明显的。 3. 如果所有的更新后面,都马上 伴随着对这个记录的查询 ,那么你应该 关闭change buffer 。而在其他情况下,change buffer都能提升更新性能。 4. 由于唯一索引用不上change buffer的优化机制,因此如果 业务可以接受 ,从性能角度出发建议优先考虑非唯一索引。但是如果"业务可能无法确保"的情况下,怎么处理呢? 首先, 业务正确性优先 。我们的前提是“业务代码已经保证不会写入重复数据”的情况下,讨论性能问题。如果业务不能保证,或业务就是要求数据库来做约束,那么没得选,必须创建唯一索引。这种情况下,本节的意义在于,如果碰上了大量插入数据慢、内存命中率低的时候,给你多提供一个排查思路。然后,在一些“ 归档库 ”的场景,你是可以考虑使用唯一索引的。比如,线上数据只需要保留半年,然后历史数据保存在归档库。这时候,归档数据已经是确保没有唯一键冲突了。要提高归档效率,可以考虑把表里面的唯一索引改成普通索引。  
EXISTS 和 IN 的区分
问题:不太理解哪种情况下应该使用 EXISTS,哪种情况应该用 IN。选择的标准是看能否使用表的索引吗?
COUNT(*)与COUNT(具体字段)效率
问:在 MySQL 中统计数据表的行数,可以使用三种方式: SELECT COUNT(*) 、 SELECT COUNT(1) 和SELECT COUNT(具体字段) ,使用这三者之间的查询效率是怎样的?
关于SELECT(*)
在表查询中,建议明确字段,不要使用 * 作为查询的字段列表,推荐使用SELECT <字段列表> 查询。原因: ① MySQL 在解析的过程中,会通过 查询数据字典 将"*"按序转换成所有列名,这会大大的耗费资源和时间。 ② 无法使用 覆盖索引    
LIMIT 1 对优化的影响
针对的是会扫描全表的 SQL 语句,如果你可以确定结果集只有一条,那么加上 LIMIT 1 的时候,当找到一条结果的时候就不会继续扫描了,这样会加快查询速度。 如果数据表已经对字段建立了唯一索引,那么可以通过索引进行查询,不会全表扫描的话,就不需要加上 LIMIT 1 了。
多使用COMMIT
只要有可能,在程序中尽量多使用 COMMIT,这样程序的性能得到提高,需求也会因为 COMMIT 所释放的资源而减少。 COMMIT 所释放的资源:   回滚段上用于恢复数据的信息   被程序语句获得的锁   redo / undo log buffer 中的空间   管理上述 3 种资源中的内部花费  

自增ID的问题

自增ID做主键,简单易懂,几乎所有数据库都支持自增类型,只是实现上各自有所不同而已。自增ID除了简单,其他都是缺点,总体来看存在以下几方面的问题: 1. 可靠性不高 存在自增ID回溯的问题,这个问题直到最新版本的MySQL 8.0才修复。 2. 安全性不高 对外暴露的接口可以非常容易猜测对应的信息。比如:/User/1/这样的接口,可以非常容易猜测用户ID的值为多少,总用户数量有多少,也可以非常容易地通过接口进行数据的爬取。 3. 性能差 自增ID的性能较差,需要在数据库服务器端生成。 4. 交互多 业务还需要额外执行一次类似 last_insert_id() 的函数才能知道刚才插入的自增值,这需要多一次的网络交互。在海量并发的系统中,多1条SQL,就多一次性能上的开销。 5. 局部唯一性 最重要的一点,自增ID是局部唯一,只在当前数据库实例中唯一,而不是全局唯一,在任意服务器间都是唯一的。对于目前分布式系统来说,这简直就是噩梦。   经验: 刚开始使用 MySQL 时,很多人都很容易犯的错误是喜欢用业务字段做主键,想当然地认为了解业务需求,但实际情况往往出乎意料,而更改主键设置的成本非常高。      

标签:使用,查询,索引,InnoDB,MySQL,WHERE,ORDER,SELECT
From: https://www.cnblogs.com/tyt0o0/p/17766856.html

相关文章

  • ubuntu 22.04安装mysql5.7
    ubuntu22.04系统安装mysql5.7一、查看系统默认安装的数据库版本apt-getupdateapt-cachepolicymysql-serverubuntu20.04自带的版本是8.0.34,而我们需要安装mysql5.7的版本就需要换源二、更换apt源1.备份原来的source文件cp/etc/apt/sources.list/etc/apt/sources......
  • .NET高性能开发-位图索引(一)
    首先来假设这样一个业务场景,大家对于飞机票应该不陌生,大家在购买机票时,首先是选择您期望的起抵城市和时间,然后选择舱等(公务舱、经济舱),点击查询以后就会出现航班列表,随意的点击一个航班,可以发现有非常多组价格,因为机票和火车票不一样,它的权益、规则更加的复杂,比如有机票中有针对......
  • update left join 在MySQL和SQL Server使用方式区别
    (1)MySQL使用UPDATEhayl_service_infot1leftjoinhayl_Old_infot2ont1.CERT_NO=t2.CERT_NOsett1.AAP0112=t2.ADDRESSwheret1.AAP0112=''(2)SQLServers使用UPDATEhayl_service_infosetAAP0112=t2.ADDRESSfromhayl_service_infot1leftjoin......
  • MySQL分区表详解
    本文已收录至GitHub,推荐阅读......
  • 多级索引结构
           ......
  • 【gdb】打印数组的索引下标
    打印数组的索引下标1.例子#include<stdio.h>intnum[10]={1<<0,1<<1,1<<2,1<<3,1<<4,1<<5,1<<6,1<<7,1<<8,1<<9};intmain(void){inti;for......
  • 【gdb】gdb目录索引
    gdb目录索引 打印1打印ASCII和宽字符字符串打印数组中任意连续元素......
  • MySQL事务隔离级别详解及应用指南
    MySQL作为关系型数据库管理系统,对于多个并发事务之间的隔离和并发控制是必不可少的。在MySQL中,提供了四种事务隔离级别,分别是:读未提交、读已提交、可重复读和串行化。读未提交在该隔离级别下,一个事务可以读取另一个并发事务未提交的数据,可能会出现“脏读”问题,即读到了未经授权的数......
  • 无涯教程-NumPy - 高级索引
    有两种类型的高级索引-Integer索引和Boolean索引。整数索引根据数组的N维索引选择数组中的任意项,每个整数数组代表该维度的索引数,当索引与目标ndarray的维数一样时,它变得很简单。示例1importnumpyasnpx=np.array([[1,2],[3,4],[5,6]])y=x[[0,1,2],[0,1......
  • Mysql分表后同结构不同名称表之间复制数据以及Update语句只更新日期加减不更改时间
    场景SpringBoot+Mybatis+定时任务实现大数据量数据分表记录和查询:SpringBoot+Mybatis+定时任务实现大数据量数据分表记录和查询_mybatis定时任务创建日表_霸道流氓气质的博客通过以上分表实现的同结构不同表名之间的表,如何将一个表中的数据复制到另一个表中,且将日期字段进行同样的......