索引失效的场景,可以查看我的另一篇博客一蓑烟雨任平生yuuu博客http://www.yuuu.online/#/DetailArticle?aid=21
下面也会提到一些,不过还是建议两者结合使用
最左匹配原则
简述最左匹配原则
最左匹配原则就是指在联合索引中,如果你的 SQL 语句中用到了联合索引中的最左边的索引,那么这条 SQL 语句就可以利用这个联合索引去进行匹配。以最左边的为起点任何连续的索引都能匹配上,同时遇到范围查询(>、<、between、like)就会停止匹配。
假设现在有一个(name,age)的索引,如果查询条件是 where age = 18,那么这条查询就不会走索引。但是如果我们加上name,此时查询条件变成 where name = ? and age = 18,又或者是where age = ? and name = ?,这样的查询都会走索引(优化器会自动调整name,age的顺序)。
为什么联合索引要满足最左匹配原则呢?
这就要说到MySQL底层的数据结构了。众所周知,MySQL底层是B+树,非叶子节点存索引,叶子节点存数据。联合索引也是如此,只不过联合索引的B+树节点存储的是键值。由于构建一棵B+树只能根据一个值来确定索引关系,所以数据库依赖联合索引最左的字段来构建。
简单来说就是,以(A,B)创建的联合索引,在B+树中,A是有序的,B是散列的;如果A是等值的,那么B就是有序的。
(1,2) (1,3) (2,1) (3,4) (3,6) 整个存储就是类似这样的数据,前为A,后为B,当A都为1,那么B就是有序的。否则单看B:2、3、1、4、6就是无序的。
补充
MySQL在8.0有了优化,最左缀原则可以通过跳跃扫描的方式打破,当第一列索引的唯一值较少时,即使where条件没有最左索引,查询的时候也可以用到联合索引。 比如我们使用的联合索引是 bcd 但是b中字段比较少 我们在使用联合索引的时候没有 使用 b 但是依然可以使用联合索引
跳过扫描访问方法适用于以下情况(8个条件):
- 表T具有至少一个复合索引,其关键部分的形式为([A_1,...,A_k]B_1, ..., B_m, C [, D_1, ..., D_n])关键部分A和D可以为空,但B和C必须为非空
- 该查询仅引用一个表
- 查询不使用GROUP BY 或 DISTINCT
- 该查询仅引用索引中的列
- A_1,...,A_k上的谓词必须是相等谓词,并且它们必须是常量。这包括
in()操作
-
该查询必须是一个联合查询。即,AND或OR条件
-
C上必须有范围条件
-
D列上的条件是允许的。D上的条件必须与C上的范围条件结合使用
Select *
-
select * 会走索引
-
范围查找有概率索引失效但是在特定的情况下会生效 范围小就会使用 也可以理解为 返回结果集小就会使用索引
-
mysql中连接查询的原理是先对驱动表进行查询操作,然后再用从驱动表得到的数据作为条件,逐条的到被驱动表进行查询
-
每次驱动表加载一条数据到内存中,然后被驱动表所有的数据都需要往内存中加载一遍进行比较。效率很低,所以mysql中可以指定一个缓冲池的大小,缓冲池大的话可以同时加载多条驱动表的数据进行比较,放的数据条数越多性能io操作就越少,性能也就越好。所以,如果此时使用
select *
放一些无用的列,只会白白的占用缓冲空间。浪费本可以提高性能的机会 -
select * 不是造成索引失效的直接原因 大部分原因是 where 后边条件的问题 但是还是尽量少去使用select * 多少还是会有影响的
使用Or导致索引失效
这个的优化方式就是 在Or的时候两边都加上索引
in使用不当
in 在结果集大于30%的时候索引失效,走全表扫描。所以使用in时,要注意查询的数据量
ORDER BY优化
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 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 /*对于排序来说,多个相等条件也是范围查询*/
filesort算法:双路排序和单路排序
- MySQL 4.1之前是使用双路排序 ,字面意思就是两次扫描磁盘,最终得到数据, 读取行指针和 order by列 ,对他们进行排序,然后扫描已经排序好的列表,按照列表中的值重新从列表中读取对应的数据输出
- 从磁盘取排序字段,在buffer进行排序,再从 磁盘取其他字段 。
- 由于单路是后出的,总体而言好过双路
- 但是用单路有问题
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会很慢
优先考虑覆盖索引
覆盖索引,简单来说就是你要查询的数据恰好包含在联合索引中,这样就避免了回表操作,减小IO。
举个例子,现在有一个用户表(User):
- id (主键)
- name
- age
- address
- phone
如果我们需要查询用户表中30岁以上的用户的姓名和邮件地址,传统的索引只能定位到满足条件的用户行,然后需要再次访问数据表以获取姓名和邮件地址的值。这将导致额外的IO操作,可能会降低查询性能,尤其是当表很大时。
使用覆盖索引,我们可以创建一个包含(age, name, email)三个列的索引。这个索引将包含查询所需的所有数据,因此数据库可以直接从索引中获取姓名和邮件地址的值,无需额外的表访问。
索引下推
使用前后的扫描过程
- id (主键)
- name
- age