在聊这个之前我们先了解下数据库的组成结构。如图:
我们经常会被问到索引有哪几种类型,多数人的第一反应是:B+tree索引、Hash索引、主键索引、唯一索引、联合索引等。这么描述也没有错,但我觉得不够准确;我个人总结MYSQL索引类型的分类其实是分几种维度的,具体如下:
B+Tree索引:
B+tree 索引是MySQL中被存储引擎采用最多的索引类型。它适用于全键值、键值范围和最左前缀查找。使用联合索引时,如果不是按照索引列的顺序进行查找,则无法使用索引。除了适用于查找,还可以用于排序和分组。
Hash索引
Hash索引是基于Hash算法实现的,它将一系列的最终的键值通过哈希函数转化为存储实际数据桶的地址数值。Hash索引在精确查询时的效率要远高于B+Tree索引,虽然Hash索引效率高,但是Hash索引本身由于其特殊性也带来了很多限制和弊端,我们一般不使用Hash索引。补充说明下:MySQL的InnoDB或MyISAM存储引擎是支持Hash索引,但是需要通过伪Hash索引来实现,叫自适应Hash索引。
全文索引
Full-text索引一般使用倒排索引实现,倒排索引同B+tree索引一样,也是一种索引结构。MySQL中InnoDB存储引擎在之前版本中是不支持全文检索的,要使用全文检索的话只能使用MyISAM存储引擎。在MySQL5.6.4版本中InnoDB存储引擎才开始支持Full-text索引。
空间索引
空间索引是对空间数据类型的字段建立的索引,创建空间索引的列,不允许为空值,且只能在 MyISAM 的表中创建。
聚簇索引
聚簇索引就是按照每张表的主键构造一颗 B+tree,一般来说,聚簇索引就是我们常说的主键索引。InnoDB表要求必须有聚簇索引,默认在主键字段上建立聚簇索引,在没有主键字段的情况下,表的第一个非空的唯一索引将被建立为聚簇索引,在前两者都没有的情况下,InnoDB将自动生成一个隐式的自增id列,并在此列上建立聚簇索引。
非聚簇索引
非聚簇索引的结构和聚集索引基本相同。其区别在于:每个索引上包含的字段内容不同,聚簇索引包含所有真实的物理数据,非聚簇索引只包含索引字段和主键字段。此外,聚簇索引一个表只能有一个,而非聚集索引一个表可以存在多个。
主键索引
建立在主键上的索引被称为主键索引,一张数据表只能有一个主键索引,索引列值不允许有空值,通常在创建表时一起创建。
唯一索引
建立在UNIQUE字段上的索引被称为唯一索引,一张表可以有多个唯一索引,索引列值允许为空,列值中出现多个空值不会发生重复冲突。
普通索引
建立在普通字段上的索引被称为普通索引。
单列索引
建立在单个列上的索引被称为单列索引。
联合索引
建立在多个列上的索引被称为联合索引,又叫复合索引、组合索引。在MySQL中使用联合索引时要遵循最左前缀匹配原则。
此处单独说明下什么是覆盖索引:
一个索引包含(或者说覆盖)所有需要查询的字段值,那么就称为覆盖索引。如果通过索引就能查询到数据,就能避免回表查询,这样能极大提高性能。日常工作中我们经常说SQL语句尽量不要用select * from table,其目的就是为了使SQL查询有机会走覆盖索引。
介绍完索引的基础信息后,接下来开始进行SQL优化的实操阶段。我们通常需要用Explain关键字查看SQL执行计划,那Explain是什么?怎么用?它有哪些参数呢?参数的指标是什么?我们具体来看看:
EXPLAIN是MySQl必不可少的一个分析工具,主要用来测试sql语句的性能及对sql语句的优化,或者说模拟优化器执行SQL语句。只要在SQL语句前加上该关键字就能查看该SQL的执行计划,其具体参数如下:
type:查询所使用的类型,从差到优依次是 all < index < range < ref < eq_ref < const;一般保证达到range,ref最好。
possible_keys:SQL语句可能用到的索引
key:SQL语句实际用到的索引
rows:查询数据所扫描的行数,rows越少越好
extra:SQL语句执行的一些额外的信息。
错误示例:
正确示例:
分析与结论:从图中可以看出,错误示例的执行计划type=all,说明查询的是全表,key=null,说明未使用索引。两个示例比较,优化前耗时需要50ms,优化后耗时只要1ms,前后查询效率相差50倍。
错误示例:
正确示例:
第五种索引失效场景:索引列参与了运算,会导致全表扫描,索引失效。
错误示例:
正确示例:
结论:如图,两个示例比较,优化前查询耗时44ms,优化后耗时只要2ms,优化前后执行效率相差22倍。
第六种索引失效场景:查询条件使用or关键字,其中一个字段没有创建单独索引,则会导致整个查询语句索引失效。
1、or关键字两边其中一个没有索引。
2、这里特别说明or两边的索引都必须是单独索引,组合索引也会导致索引失效。
3、or两边为“>”和“<”范围查询时,也会导致索引失效。
第七种索引失效场景:当查询条件使用in,not in,exist,ont exist,between and时。索引有可能失效。
1、查询条件使用not in时,如果是主键则走索引,如果是普通索引,则索引失效。
错误示例:
正确示例:
2、查询条件使用not exists时,索引失效。
3、当查询条件为">="、"in"等范围查询时,根据查询结果占全表数据比例的不同,优化器有可能会放弃索引,进行全表扫描,具体以explain分析为准。
其他索引失效场景:上述七种索引失效场景是我实际工作中常遇到的一些场景,除此之外,还有一些其他索引失效场景,如:条件使用is not null,以及Mysql优化器认为在某些情况下,全表扫描比走索引快,则它就会放弃索引。有时候索引失效并不一定代表SQL查询一定会慢,具体情况我们需要根据explain关键字具体分析。
综上所述,索引虽然能大幅度提高我们的查询数据,但是也带来了很多的负担,所以创建索引需要注意以下几点:
本文介绍了数据库调优中索引的一些相关知识,以及实际项目中索引失效的一些场景及对应优化方案,由于本文中用的测试表数据量较小,效果比对不是很明显,实际工作中大表查询语句通过索引检索查询与非索引检索的查询,SQL执行效率的差别是非常明显的。
Chen·Small-K 信也后端研发专家
Java、大数据、前端、测试等各种技术岗位热招中,欢迎扫码了解~