关系型数据库中,索引(Index)是一种用于加快数据检索速度的数据结构。它通过存储某些列的值的指针,来提高查询的效率。
一般情况下,索引是在表中的一个或多个列上创建的,它们可以显著提高查询的效率。
当我们使用 SELECT 语句查询一个表时,如果没有索引,数据库需要扫描每一行数据,这会花费大量时间,特别是对于大型的数据集。如果有了索引,数据库就可以用更快的方式定位到需要的数据行,因为它可以使用索引中存储的值来快速查找匹配的行。
索引的优缺点
优点
-
索引大大减小了服务器需要扫描的数据量,从而大大加快数据的检索速度,这也是创建索引的最主要的原因。
-
索引可以帮助服务器避免排序和创建临时表
-
索引可以将随机 IO 变成顺序 IO
-
索引对于 InnoDB(对索引支持行级锁)非常重要,因为它可以让查询锁更少的元组,提高了表访问并发性
-
InnoDB 在二级索引上使用共享锁(读锁),但访问主键索引需要排他锁(写锁)
-
通过创建唯一性索引,可以保证数据库表中每一行数据的唯一性。
-
可以加速表和表之间的连接,特别是在实现数据的参考完整性方面特别有意义。
-
在使用分组和排序子句进行数据检索时,同样可以显著减少查询中分组和排序的时间。
-
通过使用索引,可以在查询的过程中,使用优化隐藏器,提高系统的性能。
缺点
虽然索引可以显著提高查询的效率,但也会增加写入操作的开销。因为每次写入操作都需要更新索引,这可能导致写入操作比没有索引的情况下慢一些。因此,在创建索引时需要权衡查询效率和写入效率之间的权衡。
-
创建索引和维护索引要耗费时间,这种时间随着数据量的增加而增加
-
索引需要占用物理空间,除了数据表占用数据空间之外,每一个索引还要占用一定的物理空间,如果需要建立聚簇索引,那么需要占用的空间会更大
-
对表中的数据进行增、删、改的时候,索引也要动态的维护,这就降低了整数的维护速度
-
如果某个数据列包含许多重复的内容,为它建立索引就没有太大的实际效果。
-
对于非常小的表,大部分情况下简单的全表扫描更高效;
创建索引的准则
索引是建立在数据库表中的某些列的上面。因此,在创建索引的时候,应该仔细考虑在哪些列上可以创建索引,在哪些列上不能创建索引。
需要创建索引的列
-
在经常需要搜索的列上,可以加快搜索的速度
-
在作为主键的列上,强制该列的唯一性和组织表中数据的排列结构
-
在经常用在连接(
JOIN
)的列上,这些列主要是一些外键,可以加快连接的速度 -
在经常需要根据范围(<,<=,=,>,>=,BETWEEN,IN)进行搜索的列上创建索引,因为索引已经排序,其指定的范围是连续的
-
在经常需要排序(order by)的列上创建索引,因为索引已经排序,这样查询可以利用索引的排序,加快排序查询时间;
-
在经常使用在
WHERE
子句中的列上面创建索引,加快条件的判断速度。
无需创建索引的列
-
对于那些在查询中很少使用或者参考的列不应该创建索引。
若列很少使用到,因此有索引或者无索引,并不能提高查询速度。相反,由于增加了索引,反而降低了系统的维护速度和增大了空间需求。
-
对于那些只有很少数据值或者重复值多的列也不应该增加索引。
这些列的取值很少,例如人事表的性别列,在查询的结果中,结果集的数据行占了表中数据行的很大比例,即需要在表中搜索的数据行的比例很大。增加索引,并不能明显加快检索速度。
-
对于那些定义为 TEXT, IMAGE 和 BIT 数据类型的列不应该增加索引。这些列的数据量要么相当大,要么取值很少。
-
当该列修改性能要求远远高于检索性能时,不应该创建索引。(修改性能和检索性能是互相矛盾的)
索引分类
MySQL 的索引有两种分类方式:逻辑分类和物理分类。
逻辑分类
按逻辑分类有多种逻辑划分的方式,如按功能划分,按组成索引的列数划分等。
按列数划分
单例索引
单例索引:在数据库中为单个列创建的索引。一个索引只包含一个列,一个表可以有多个单例索引。
单例索引的特点和用途包括:
-
提高查询性能:通过为单个列创建索引,可以加快根据该列进行的搜索和排序操作的速度。当查询条件涉及到单个列时,单例索引可以显著提高查询性能。
-
索引大小较小:相比于多列索引,单例索引只涉及一个列,因此索引本身的大小往往较小。这可以减少索引的存储空间占用,并提高索引的缓存效率。
-
精确匹配能力:单例索引适用于需要进行精确匹配的查询,例如等值查询。通过单例索引,可以快速定位到符合查询条件的记录。
-
不适用范围查询:由于单例索引只包含一个列的索引信息,所以对于范围查询(如大于、小于等)的效果并不理想。范围查询可能需要扫描更多的索引页,导致查询性能下降。
使用单例索引时需要权衡索引的选择,因为过多地创建索引可能会造成存储空间的浪费和性能的下降。
组合索引(多列索引)
组合索引:一个组合索引包含两个或两个以上的列。查询的时候遵循 MySQL 组合索引的 “最左前缀”原则,即使用 WHERE
时条件要按照建立索引的时候字段的排列方式放置索引才会生效。
组合索引的主要特点和用途包括:
-
支持复合查询:组合索引可以为复合查询提供支持,即基于多个列进行查询和排序操作。例如,可以根据 用户ID 和 电影评分 两个列进行查询,以找到用户评分最高的电影列表。
-
提高查询性能:针对复合查询,可以通过创建组合索引来提高查询性能。由于该索引涉及多列数据,因此可以在多个列之间建立更紧凑、更高效的数据结构,减少扫描数据的次数。
-
减少索引数量:相对于每个列都创建单独的索引,组合索引可以减少所需索引的数量,并减少索引维护的成本,提高查询性能。
-
顺序匹配:组合索引会按照指定的列顺序存储数据,因此只有使用索引的前缀列时,查询才能够利用到索引。例如,如果索引顺序是(用户ID,电影评分),那么只有查询时涉及到 用户ID 列时,才能够利用索引进行匹配。
按功能划分
主键索引
主键索引:针对主键列(PRIMARY KEY)建立的索引,保证主键的唯一性和查询效率。一张表只能有一个主键索引,不允许重复、不允许为 NULL
其优点是方便高效地查找数据并确保数据的唯一性,因为数据库会对主键自动建立索引。主键索引适用于对表进行快速查询、排序、合并等操作。
唯一索引
唯一索引:针对唯一性列(UNIQUE)建立的索引,同样保证列的唯一性和查询效率。数据列不允许重复,允许为 NULL 值,一张表可有多个唯一索引,索引列的值必须唯一,但允许有空值。如果是组合索引,则列值的组合必须唯一。
和主键索引相比,唯一索引并不自动成为主键。唯一索引适用于需要保证某个列的数据唯一性而不需要建立主键的情况。
普通索引
普通索引:普通索引也称为非唯一索引,用于提高查询效率。一张表可以创建多个普通索引,一个普通索引可以包含多个字段,允许数据重复,允许 NULL 值插入;
普通索引不要求列的数据唯一性。使用普通索引可以加快查询速度,但需要权衡建立索引所带来的开销和维护索引所需的时间,因为索引可以占用较大的数据库存储空间。
全文索引
全文索引:全文索引用于对文本类型的数据进行搜索。
这种索引可以在文本列中匹配单词等细节性内容,而不仅仅是与整个文本比较。全文索引适用于需要对文本类型数据进行全面搜索和匹配的场景,如博客、论坛和搜索引擎等。
全文索引的主要特点和用途包括:
-
文本搜索能力:全文索引可以处理文本数据中的字词、短语和自然语言查询,而不仅仅是简单的单词匹配。它支持模糊搜索、通配符搜索、拼写纠错和同义词扩展等功能。
-
搜索效率提升:通过使用全文索引,可以大大提高对大量文本数据进行搜索操作的效率。相对于传统的基于模式匹配的搜索方法,全文索引可以更快地找到匹配的结果。
-
支持多语言:全文索引技术可以应用于不同语言的文本数据,并提供对多语言搜索的支持。它可以处理不同的字符集、词法和语法规则等。
-
排序和权重:全文索引可以对搜索结果进行排序和打分,以便更好地匹配用户的查询意图。它可以根据搜索词的出现频率、位置和关联性等因素为结果赋予权重。
-
高级搜索功能:全文索引还支持高级搜索功能,如范围搜索、逻辑运算符(与、或、非)的组合以及近似搜索等。这些功能可以进一步提升搜索的精度和灵活性。
空间索引 SPTIAL
空间索引(Spatial Index)是一种用于加速地理空间数据查询的数据结构。它主要用于管理和优化在二维或三维空间中存储的地理空间数据,如点、线、面等。
传统的索引结构,比如B树或哈希索引,无法直接应用于地理空间数据的查询,因为这些数据具有不同的特点:位置相关性、多维性和范围查询等需求。而空间索引就是为了满足这些特点而设计的。
常见的空间索引结构包括:
-
R树(R-tree):R树 是一种多维索引结构,被广泛应用于空间数据的管理。它可以用于范围查询、最近邻查询、相交查询等地理空间查询。R树可以将地理空间数据组织成一个树形结构,通过节点间的相互关系,减少不必要的搜索和访问。
-
Quadtree:Quadtree 是一种基于四叉树的空间索引结构。它将空间划分为四个象限,每个象限又划分成四个子象限,以此类推,直到划分到某一条件满足为止。Quadtree主要用于场景的近似匹配查询,比如找到与一个给定区域相交的数据。
-
KD-tree:KD-tree 是一种二叉树结构,用于高维空间数据的索引。它将空间按照垂直切割的方式构建树结构,每个节点存储一个数据点,并根据数据点的位置在空间中进行切割。KD-tree主要用于最近邻查询和范围查询。
除了以上几种常见的空间索引结构,还有很多其他的索引结构,如 R+Tree、Hilbert R树 等。选择合适的空间索引结构要考虑数据的特征、查询需求和系统性能等方面的因素。
物理分类
按照物理分类,索引可以分为聚簇索引(也称主索引)和非聚簇索引(也称辅助索引或二级索引)
聚簇索引(主索引)
聚簇索引(Clustered Index)是一种特殊的数据库索引,它是根据表的某一列(聚簇键)的数据顺序建立的,而不是像普通索引那样是按索引列排列数据。
聚簇索引不是单独的一种索引类型,而是一种数据存储方式。这种存储方式是依靠 B+Tree
来实现的,根据表的主键构造一棵 B+树 且 B+树 叶子节点存放的都是表的行记录数据时,方可称该主键索引为聚簇索引。也可理解为将数据存储与索引放到了一块,找到索引也就找到了数据。
聚簇索引的优势在于,数据的物理存储顺序和聚簇索引的排序顺序是一致的,即相邻的记录在物理上也是相邻的,这样可以最大程度地提高查询效率,尤其对于主键的排序查找和范围查找速度非常快,大大减少磁盘 I/O 操作。
聚簇
聚簇是为了提高某个属性(或属性组)的查询速度,把这个或这些属性(称为聚簇码)上具有相同值的元组集中存放在连续的物理块。
当通过聚簇码进行访问或连接是该关系的主要应用,与聚簇码无关的其他访问很少或者是次要的,这时可以使用聚簇。
建立聚簇
一个数据库可以建立多个聚簇,一个关系只能加入一个聚簇。选择聚簇存取方法,即确定需要建立多少个聚簇,每个聚簇中包括哪些关系
首先设计候选聚簇,一般来说:
- 对经常在一起进行连接操作的关系可以建立聚簇。
- 如果一个关系的一组属性经常出现在相等比较条件中,则该单个关系可建立聚簇
- 如果一个关系的一个(或一组)属性上的值重复率很高,则此单个关系可建立聚即对应每个聚簇码值的平均元组数不能太少,太少则聚簇的效果不明显。
然后检查候选聚簇中的关系,取消其中不必要的关系:
-
从聚簇中删除经常进行全表扫描的关系。
-
从聚簇中删除更新操作远多于连接操作的关系。
-
不同的聚簇中可能包含相同的关系,一个关系可以在某一个聚簇中,但不能同时加入多个聚簇。要从这多个聚簇方案(包括不建立聚簇)中选择一个较优的,即在这个聚簇上运行各种事务的总代价最小。
注意事项
使用聚簇索引有以下几个注意点:
-
一张表只能有一个聚簇索引,因为聚簇索引会影响数据的物理存储。
-
聚簇索引的建立需要考虑聚簇键的选择,通常要选择唯一性较高的列作为聚簇键。
-
更新聚簇索引列的开销较大,因为更新操作可能会引起数据的物理移动,并使此关系上原来建立的所有索引无效,必须重建。因此在对聚簇索引列进行大量更新操作的场景中,建议不要使用聚簇索引。
-
当一个元组的聚簇码值改变时,该元组的存储位置也要做相应移动,聚簇码值要相对稳定,以减少修改聚簇码值所引起的维护开销。
-
聚簇索引和非聚簇索引(即普通索引)的建立是互不影响的,可以在相同的表上同时使用这两种索引。
-
其当 SQL 语句中包含有与聚簇码有关的
ORDER BY
、GROUP BY
、UNION
、DISTINCT
等子句或短语时,使用聚簇特别有利,可以省去对结果集的排序操作;否则很可能会适得其反。
缺点
-
插入速度严重依赖于插入顺序,按照主键的顺序插入是最快的方式,否则将会出现页分裂,严重影响性能。
因此,对于 InnoDB 表,我们一般都会定义一个自增的 ID 列为主键(主键列不要选没有意义的自增列,选经常查询的条件列才好,不然无法体现其主键索引性能)
-
更新主键的代价很高,因为将会导致被更新的行移动。因此对于InnoDB 表,我们一般定义主键为不可更新。
非簇状索引(辅助索引)
非聚簇索引(Non-Clustered Index)是在表的某一列上建立的索引,与聚簇索引不同,非聚簇索引并不改变数据的物理存储顺序。
非聚簇索引通过创建一个独立的数据结构,使得查询操作更加高效。它包含索引列的值以及指向相应数据行的指针或物理地址。
以下是关于非聚簇索引的一些要点:
-
一张表可以有多个非聚簇索引,通过在不同的列上建立索引,可以更灵活地支持不同的查询操作。
-
非聚簇索引可以加快数据检索的速度,因为查询操作首先通过索引找到相关的记录,然后再通过指针或物理地址找到具体的数据行。
-
非聚簇索引对于频繁更新的表更加友好,因为它的更新操作不需要像聚簇索引一样移动数据行。
-
在某些情况下,查询操作需要访问多个非聚簇索引来获取完整的结果集,这可能会导致额外的 I/O 开销。
-
二级(辅助)索引访问需要两次索引查找,第一次找到主键值,第二次根据主键值找到行数据。
索引的底层数据结构
索引是在存储引擎层实现的,而不是在服务器层实现的,所以不同存储引擎具有不同的索引类型和实现。
MySQL 中常用的索引结构(索引底层的数据结构)有:B树
,B+树
,HASH
等。
索引是对记录集的多个字段进行排序的方法。在一张表中为一个字段创建一个索引,将创建另外一个数据结构,包含字段数值以及指向相关记录的指针,然后对这个索引结构进行排序,允许在该数据上进行二分法排序。将时间复杂度缩小到 O ( l o g 2 N ) O(log_2N) O(log2N)
B-Tree
B-Tree
(B树)是一种用于存储和组织数据的平衡搜索树数据结构,其设计目标是提供高效的插入、删除和查找操作,同时能够适应频繁的数据更新和范围查询。
特点
B-Tree 的特点包括:
-
平衡性:B-Tree 是一棵平衡树,它的所有叶子节点都位于相同的深度,这样可以确保树的高度相对较小,使得查找操作的时间复杂度保持在较低的水平。
-
多路性:B-Tree 的每个节点可以存储多个键值对,也就是多个子节点的指针。这样可以减少树的高度,提高查询效率。
-
有序性:B-Tree 中的节点是按照键值的顺序存储的,使得范围查询操作更加高效。
-
磁盘友好性:B-Tree 的设计考虑了磁盘块的读写特性,节点的大小通常和磁盘块的大小相当,树高一层意味着多一次的磁盘 I/O,从而可以最大限度地减少磁盘 IO 操作。
特征
- 关键字集合分布在整颗树中;
- 任何一个关键字出现且只出现在一个结点中;
- 搜索有可能在非叶子结点结束;
- 其搜索性能等价于在关键字全集内做一次二分查找;
- 自动层次控制;
B+ Tree
B+Tree
是大多数 MySQL 存储引擎的默认索引类型。InnoDB 和 MyISAM 存储引擎都默认使用 B+树 结构存储索引
因为不再需要进行全表扫描,只需要对树进行搜索即可,因此查找速度快很多。除了用于查找,还可以用于排序和分组。
可以指定多个列作为索引列,多个索引列共同组成键。
适用于全键值、键值范围和键前缀查找,其中键前缀查找只适用于最左前缀查找。如果不是按照索引列的顺序进行查找,则无法使用索引。
InnoDB 的 B+Tree
索引分为 聚簇索引(主索引) 和 非簇状索引(辅助索引)。
主索引的叶子节点 data 域记录着完整的数据记录,这种索引方式被称为聚簇索引。因为无法把数据行存放在两个不同的地方,所以一个表只能有一个聚簇索引。
辅助索引的叶子节点的 data 域记录着主键的值,因此在使用辅助索引进行查找时,需要先查找到主键值,然后再到主索引中进行查找。
只有 InnoDB 的主键索引才是聚簇索引,InnoDB 中的辅助索引以及 MyISAM 使用的都是非聚簇索引。
特征
-
所有关键字都出现在叶子结点的链表中(稠密索引),且链表中的关键字恰好是有序的;
-
不可能在非叶子结点中;
-
非叶子结点相当于是叶子结点的索引(稀疏索引),叶子结点相当于是存储(关键字)数据的数据层;
-
每一个叶子节点都包含指向下一个叶子节点的指针,从而方便叶子节点的范围遍历。
-
更适合文件索引系统;
B+ 索引存取方法的选择
-
如果一个(或一组)属性经常在查询条件中出现,则考虑在这个(或这组)属性上建立索引(或组合索引)。
-
如果一个属性经常作为最大值和最小值等聚集函数的参数,则考虑在这个属性上建立索引。
-
如果一个(或一组)属性经常在连接操作的连接条件中出现,则考虑在这个(或这组)属性上建立索引。
关系上定义的索引数并不是越多越好,系统为维护索引要付出代价,查找索引也要付出代价。例如,若一个关系的更新频率很高,这个关系上定义的索引数不能太多。因为更新一个关系时,必须对这个关系上有关的索引做相应的修改。
Hash
哈希索引,采用一定的哈希算法,把键值换算成新的哈希值,检索时不需要像 B+Tree
那样从根节点到叶子节点逐级查找,只需一次哈希算法即可立刻定位到相应的位置,能以
O
(
1
)
O(1)
O(1) 时间进行查找,但是失去了有序性,它具有以下限制:
- 无法用于排序与分组;
- 只支持精确查找,仅仅能满足
=
=
=,IN,
<
=
>
<=>
<=> 查询,无法用于部分查找和范围查找,例如
WHERE age>18
。
由于 Hash 索引比较的是进行 Hash 运算之后的 Hash 值,所以它只能用于等值的过滤,不能用于基于范围的过滤,因为经过相应的 Hash 算法处理之后的 Hash 值的大小关系,并不能保证和 Hash 运算前完全一样。
InnoDB 存储引擎有一个特殊的功能叫“自适应哈希索引”,当某个索引值被使用的非常频繁时,会在 B+Tree
索引之上再创建一个哈希索引,这样就让 B+Tree
索引具有哈希索引的一些优点,比如快速的哈希查找。
Hash 索引存取方法的选择
如果一个关系的属性主要出现在等值连接条件中,或主要出现在等值比较选择条件中,而且满足下列两个条件之一,则此关系可以选择 Hash 存取方法。
- 一个关系的大小可预知,而且不变。
- 关系的大小动态改变,但数据库管理系统提供了动态 hash 存取方法
全文索引
MyISAM 存储引擎支持全文索引,用于查找文本中的关键词,而不是直接比较是否相等。查找条件使用 MATCH AGAINST
,而不是普通的 WHERE
。
全文索引一般使用倒排索引实现,它记录着关键词到其所在文档的映射。
InnoDB 存储引擎在 MySQL 5.6.4 版本中也开始支持全文索引。
R-Tree
MyISAM 存储引擎支持空间数据索引 R-Tree
,可以用于地理数据存储。空间数据索引会从所有维度来索引数据,可以有效地使用任意维度来进行组合查询。
必须使用 GIS
相关的函数来维护数据。
索引存储在文件系统中
索引是占据物理空间的,在不同的存储引擎中,索引存在的文件也不同。存储引擎是基于表的,以下分别使用 MyISAM 和 InnoDB 存储引擎建立两张表。
存储引擎为 MyISAM:
*.frm
:与表相关的元数据信息都存放在 frm 文件,包括表结构的定义信息等
*.MYD
:MyISAM DATA,用于存储 MyISAM 表的数据
*.MYI
:MyISAM INDEX,用于存储 MyISAM 表的索引相关信息
存储引擎为 InnoDB:
*.frm
:与表相关的元数据信息都存放在 frm 文件,包括表结构的定义信息等
*.ibd
:InnoDB DATA,表数据和索引的文件。该表的索引(B+树)的每个非叶子节点存储索引,叶子节点存储索引和索引对应的数据
总结
MySQL索引是优化数据库查询性能的重要手段。通过合理选择索引类型、避免索引冗余、限制索引数量以及合理使用联合索引,可以显著提高数据库的查询效率。然而,索引并非越多越好,过多的索引会增加写操作的负担,因此在设计索引时需要综合考虑各种因素。
参考链接:
MySQL - 索引(B+树) | Java 全栈知识体系 (pdai.tech)