一、索引概述
索引是表的目录,在查找内容之前可以先在目录中查找索引位置,以此快速定位查询数据。对于索引,会保存在额外的文件中。
索引是帮助MySQL高效获取数据的排好序的数据结构。
平时代码中为了方便查询,我们使用的哈希表、链表、二叉树等数据结构存储相关数据,使用二分、二叉搜索等算法查询,本质上数据库也是类似这种方式,只是数据库要处理的数据量很多、对实时性也有较高要求,这自然要针对具体场景选择最适合的索引方式。
1.1 使用场景
经常需要作为条件查询的列上适合创建索引,并且该列上也必须有一定的区分度。
索引需要空间来存储,也需要定期维护, 每当有记录在表中增减或索引列被修改时,索引本身也会被修改,这意味着每条记录的INSERT,DELETE,UPDATE将为此多付出4,5 次的磁盘I/O,因为索引需要额外的存储空间和处理,那些不必要的索引反而会使查询反应时间变慢。
使用索引查询不一定能提高查询性能,索引范围查询(INDEX RANGE SCAN)适用于两种情况:
- 基于一个范围的检索,一般查询返回结果集小于表中记录数的30%。
- 基于非唯一性索引的检索。
索引开销:
- 维护开销:当对表中的数据进行增加、删除和修改的时候,索引也要动态的维护,这样就降低了数据的维护速度。
- 存储开销:除了数据表占数据空间之外,每一个索引还要占一定的物理空间,如果要建立聚簇索引,那么需要的空间就会更大。
- 时间开销:创建索引和维护索引要耗费时间,这种时间随着数据量的增加而增加。
滥用索引的情况很常见,查询时恨不得每个字段都要有索引,但索引本身也是负担,其消耗的计算和存储资源与数据量成正比
建索引前需要慎重考虑表的数据量、查询相关的业务场景
1.2 Mysql支持的索引实现
-
B+Tree索引:B-Tree索引是MySQL中最常见的索引实现方式之一。B-Tree索引适用于范围查询和精确匹配,并且可以按照指定的排序规则进行排序。主要用于InnoDB存储引擎。
-
哈希索引:哈希索引适用于进行快速的等值匹配查询,但不支持范围查询和排序。哈希索引适用于Memory存储引擎,并且只能在内存中使用。
-
全文索引:全文索引用于在文本数据中进行全文搜索。MyISAM存储引擎支持全文索引,它使用倒排索引来加速搜索。
-
空间索引:空间索引主要用于处理空间数据,如地理位置数据。MyISAM和InnoDB存储引擎都支持空间索引。
-
前缀索引:前缀索引可以选择只索引字符串的一部分,以节省存储空间。它对于长字符串和大数据集很有用,但会牺牲一定的查询性能。
-
组合索引:组合索引是指同时在多个列上创建的索引。它可以提高多列条件查询的性能,但在查询时需要注意索引列的顺序。
-
全文索引:全文索引适用于MyISAM存储引擎。它使用倒排索引来加速文本的全文搜索。全文索引可以查找包含特定关键词的文本。
虽然MySQL支持多种索引类型,但不同的存储引擎可能对索引的支持有所不同。默认情况下,MySQL使用InnoDB存储引擎,所以B+tree索引是主要的索引实现方式。
我们平时使用的基本都是InnoDB存储引擎,所以B+tree是真正需要理解的索引实现。
二、索引的不同数据结构
B+tree也就是当前InnoDB存储引擎的索引存储结构
具体的数据结构详解就不在这里展开,这里说明下为何最终会选择B+tree的结构,其他常规的结构和B+tree相比有什么不足
2.1 Hash表
- 虽然可以快速定位,但是没有顺序,IO复杂度高。
- 适合等值查询,如=、in()、<=>,不支持范围查询;
- 如果有大量重复键值得情况下,哈希索引的效率会很低,因为存在哈希碰撞问题
2.2 二叉树
- 常规二叉树:树的高度不均匀,不能自平衡,查找效率跟数据有关(树的高度),并且IO代价高。
- 红黑树:树的高度随着数据量增加而增加,IO代价高。
- 平衡二叉树:深度太大(因为一个节点最多只有2个子节点),一次查询需要的I/O复杂度为O(lgN),平衡二叉树逻辑上很近的父子节点,物理上可能很远,无法充分发挥磁盘顺序读和预读的高效特性。
2.3 Btree
可以说B+tree是对Btree的改造,使其更能满足数据库的索引需求
Btree的不足在于B树不管叶子节点还是非叶子节点,都会保存数据,这样导致在非叶子节点中能保存的指针数量变少(有些资料也称为扇出),指针少的情况下要保存大量数据,只能增加树的高度,导致IO操作变多,查询性能变低
Btree的结构图:
2.4 B+tree
B+tree也就是当前InnoDB存储引擎的索引存储结构,至少是当前能满足mysql索引查询需求的实现方式
B+tree的结构图:
- B+tree的内部节点通常存储索引键值,叶子节点存储索引的键值和指向实际数据行的指针(或者称为RID)
- B+树索引中的键值默认按升序排列。可以通过定义索引时的排序规则(ASC或DESC)来改变排序顺序
- B+树的内部节点通常不包含实际数据,它们用于导航到对应的叶子节点。叶子节点包含实际的索引键值和指向数据行的指针
结合B+Tree的特点,自增主键是连续的,在插入过程中尽量减少页分裂,即使要进行页分裂,也只会分裂很少一部分。并且能减少数据的移动,每次插入都是插入到最后。总之就是减少分裂和移动的频率。
三、联合索引
MySQL的联合索引是指在一个索引中包含多个列的索引。
它允许在查询中同时使用多个列进行搜索,提高查询的效率。
联合索引也是基于B+tree的结构,如下图所示:
3.1 联合索引的顺序
联合索引经常在使用时忽略顺序,不少初学者认为有了联合索引,只要查询时有索引字段可以,这是错误的
- 查询时从左往右比较字段,当第一个字段相同就比较第二个字段,以此类推。
- 查询时只有按顺序从左往右使用联合索引设置的字段才能走索引进行查询。
例如
联合索引: name age position
查询时使用字段:
name age --使用索引
name postion --name使用索引, position不使用
age postion --不使用索引
3.2 最左前缀原则
- 索引的最左前缀原则是指在创建索引时,索引的字段顺序应该按照查询条件中最常用的字段顺序进行排序。这样可以最大程度地提高查询效率。
- 这个原则的目的是减少索引的大小,提高查询性能,并减少磁盘I/O的开销。因此,在设计数据库表结构时,应该根据实际的查询需求来确定索引的字段顺序。
具体事例:
- 如果查询条件经常使用字段A和字段B进行筛选,那么在创建索引时应该将字段A放在字段B之前,以便能够充分利用索引的最左前缀匹配。
- mysql会一直向右匹配直到遇到范围查询(>、<、between、like)就停止匹配,比如a = 1 and b = 2 and c > 3 and d = 4 如果建立(a,b,c,d)顺序的索引,d是用不到索引的,如果建立(a,b,d,c)的索引则都可以用到,a,b,d的顺序可以任意调整。
- =和in可以乱序,比如a = 1 and b = 2 and c = 3 建立(a,b,c)索引可以任意顺序,mysql的查询优化器会帮你优化成索引可以识别的形式。
3.3 前缀索引
- 前缀索引是一种索引类型,它允许只对字段值的前几个字符进行索引,而不是对整个字段值进行索引。
- 这种索引类型索引的列字符很多的情况时下提高查询性能和减少索引占用的存储空间。
- 缺点就是:MySQL无法使用前缀索引做ORDER BY 、GROUP BY 和覆盖扫描,前缀索引也可能会导致索引的选择性下降,从而影响查询性能。
前缀索引创建语句:
CREATE INDEX index_name ON table_name (column_name(length));
--其中,index_name是索引的名称,table_name是表名,column_name是字段名,length是前缀长度。
一般很少使用前缀索引,除非查询场景非它不可
四、索引失效
当然不是所有查询条件都走索引,以下情况索引就无效了:
- 查询条件为
!=
<
>
NOT IN
NOT EXISTS
- 类型不一致,如表中为bigint类型,查询是用varchar
- 函数:如果使用函数在索引列,这是不走索引的
- 运算符:如果你对列进行了(+,-,*,/,!), 那么都将不会走索引。
- OR:OR导致索引是在特定情况下的,并不是所有的OR都是使索引失效,如果OR连接的是同一个字段,那么索引不会失效,如果不同索引失效。
- 模糊搜索:当
%
放在匹配字段前是不走索引的,放在后面才会走索引。