索引是什么
1.索引的分类:
1.1.索引的分类:
MySQL索引包括普通索引,唯一索引,全文索引,单列索引,多列索引,和空间索引等
- 1.从功能逻辑上说,索引包括四种:
- 普通索引
- 唯一索引
- 主键索引
- 全文索引
- 2.按照物理实现方式来说,索引可以分
- 聚簇索引
- 非聚簇索引
- 3.按照作用的字段个数来划分:
- 单列索引
- 联合索引
1.2.按照逻辑划分来介绍各索引:
a.普通索引:
- 1.在创建普通索引时,不附加任何限制条件,只是用于提高查询效率
- 2.这类索引可以创建在任何数据类型中,其值是否唯一和非空,要由字段本身的完整性约束条件决定。
- 3.建立索引以后,可以通过索引进行查询
- 4.例如,
在表 student 的字段 name 上建立一个普通索引
,查询记录时就可以根据该索引进行查询
b.唯一性索引:
- 1.使用
UNIQUE 参数
可以设置索引为唯一性索引,在创建唯一性索引时,限制该索引的值必须是唯一的,但允许有空值
- 2.在一张数据表里可以有多个唯一索引
- 3.例如,在表 student 的字段 email 中创建唯一性索引,那么字段 email 的值就必须是唯一的。
- 4.通过唯一性索引,可以更快速地确定某条记录
c.主键索引:
- 1.主键索引就是一种特殊的唯一性索引,在
唯一索引的基础上增加了不为空的约束
,也就是NOTNULL + UNIQUE
- 2.一张表里最多只有一个主键索引。这是由主键索引的物理实现方式决定的,因为数据存储在文件中只能按照一种顺序进行存储
d.全文索引:
- 1.全文索引(也称全文检索)是
目前搜索引擎
使用的一种关键技术。它能够利用分词技术等多种算法智能分析出文本文字中关键词的频率和重要性,然后按照一定的算法规则智能地筛选出我们想要的搜索结果。全文索引非常适合大型数据集,对于小的数据集,它的用处比较小。 - 2.使用
参数 FULLTEXT
可以设置索引为全文索引。在定义索引的列上支持值的全文查找,允许在这些索引列中插入重复值和空值。全文索引只能创建在 CHAR、VARCHAR 或 TEXT 类型及其系列类型的字段上
,查询数据量较大的字符串类型的字段时,使用全文索引可以提高查询速度。例如,表 student 的字段 information 是 TEXT 类型,该字段包含了很多文字信息。在字段 information 上建立全文索引后,可以提高查询字段 information 的速度 - 3.全文索引典型的有两种类型:
自然语言的全文索引和布尔全文索引
。- 自然语言搜索引擎将计算每一个文档对象和查询的相关度。这里,相关度是基于匹配的关键词的个数,以及关键词在文档中出现的次数。在整个索引中出现次数越少的词语,匹配时的相关度就越高。
- 相反,非常常见的单词将不会被搜索,如果一个词语的在超过 50% 的记录中都出现了,那么自然语言的搜索将不会搜索这类词语。
- 4.MySQL数据库从 3.23.3 版开始支持全文索引,但 MySQ L5.6.4 以前只有 MyISAM 支持,5.6.4 版本以后 InnoDB 才支持,但是
官方版本不支持中文分词
,需要第三方分词插件。在 5.7.6 版本,MySQL 内置了 ngram 全文解析器
,用来支持亚洲语种的分词。测试或使用全文索引时,要先看一下自己的 MySQL 版本、存储引擎和数据类型是否支持全文索引。而随着大数据时代的到来,关系型数据库应对全文索引的需求已力不从心,逐渐被 solr、ElasticSearch 等专门的搜索引擎所替代
。
1.3.按照字段个数划分来介绍索引:
a.单列索引:
- 1.在表中的
单个字段上
创建索引。单列索引只根据该字段进行索引。单列索引可以是普通索引,也可以是唯一性索引,还可以是全文索引 - 2.只要保证该索引只对应一个字段即可。一个表可以有多个单列索引
b.多列(组合、联合)索引:
- 1.多列索引是在表的多个字段组合上创建一个索引
- 2.该索引指向创建时对应的多个字段,可以通过这几个字段进行查询,但是只有查询条件中使用了这些字段中的第一个字段时才会被使用
- 3.例如,在表中的字段 id、name 和 gender 上建立一个多列索引 idx_id_name_gender,只有在查询条件中使用了字段 id 时该索引才会被使用。
使用组合索引时遵循最左前缀集合
1.3.空间索引:
- 1.使用
参数 SPATIAL
可以设置索引为空间索引。 - 2.空间索引只能建立在空间数据类型上,这样可以提高系统获取空间数据的效率。
- 3.MySQL 中的空间数据类型包括
GEOMETRY、POINT、LINESTRING 和 POLYGON
等。 - 4.目前只有 MyISAM 存储引擎支持空间检索,而且索引的字段不能为空值。对于初学者来说,这类索引很少会用到
2.各存储引擎对索引类型的的支持情况:
- 1.各个存储引擎对索引支持情况总结:
1.索引是什么:
- 1.索引是
存储引擎用于快速查找数据记录的一种数据结构
,索引好比一本书的目录部分,可以通过目录找到对应文章的页码,便可以快速定位需要的文章,MySQL中索引与书本的目录是一样的道理,在数据查找的时候,首先查看查询条件是否命中某条索引,符合索引则查找相关的数据,如果不符合则需要全表扫描,即需要一条一条的查找记录
,直到找到与条件符合的记录为止
2.索引分类:
- 1.索引按照物理实现方式分类,索引可以分为 2 种:
聚簇(聚集)
:针对主键构建的索引称之为聚簇索引非聚簇(非聚集)索引
:也把非聚集索引称为二级索引或者辅助索引
,是针对非主键构建的索引
3.聚簇索引是什么:
3.1.聚簇索引概念:
- 1.聚簇索引并不是一种单独的索引类型,而
是一种数据存储方式(所有的用户记录都存储在叶子节点
),也就是索引即数据,数据即索引
,聚簇表示的就是数据行和相邻的键值聚簇的存储在一起
3.2.聚簇索引特点:
- 1.使用记录主键值的大小进行记录和页的排序,这包括三个方面的含义:
页内
的记录是按照主键的大小
顺序排成一个单向链表
。- 各个存放
用户记录的页
也是根据页中用户记录的主键大小顺序排成一个双向链表
。 - 存放
目录项记录
的页 分为不同的层次,在同一层次中的页也是根据页中目录项记录的主键大小顺序排成一个双向链表
。
- 2.B+树的 叶子节点 存储的是完整的用户记录。所谓完整的用户记录,就是指这个记录中存储了所有列的值(包括隐藏列)
- 我们把具有上述这两种特性的 B+ 树称为聚簇索引,所有完整的用户记录都存放在这个聚簇索引的叶子节点处。
这种聚簇索引并不需要我们在MySQL 语句中显式的使用 INDEX 语句去创建,InnoDB 存储引擎会自动的为我们创建聚簇索引
3.3.聚簇索引优点:
- 1.
数据访问更快
,因为聚簇索引将索引和数据保存在同一个B+树
中,因此从聚簇索引中获取数据比非聚簇索引更快 - 2.聚簇索引对于主键的
排序查找
和范围查找
速度非常快 - 3.按照聚簇索引排列顺序,查询显示一定范围数据的时候,由于数据都是紧密相连,数据库不用从多个数据块中提取数据,所以
节省了大量的io操作
3.4.聚簇索引缺点:
- 1.
插入速度严重依赖于插入顺序
,按照主键的顺序插入是最快的方式,否则将会出现页分裂,严重影响性能。因此,对于InnoDB表,我们一般都会定义一个自增的ID列为主键
- 2.
更新主键的代价很高
,因为将会导致被更新的行移动。因此,对于InnoDB表,我们一般定义主键为不可更新 - 3.二级索引访问需要两次索引查找 ,第一次找到主键值,第二次根据主键值找到行数据
3.5.聚簇索引限制:
- 1.对于 MySQL 数据库目前只有 InnoDB 数据引擎支持聚簇索引,而 MylSAM 并不支持聚簇索引。
- 2.由于数据物理存储排序方式只能有一种,所以
每个 MySQL 的表只能有一个聚簇索引
。一般情况下就是该表的主键。 - 3
.如果没有定义主键,InnoDB 会选择非空的唯一索引代替
。如果没有这样的索引,InnoDB 会隐式的定义一个主键来作为聚簇索引。 - 4.为了充分利用聚簇索引的聚簇的特性,所以
InnoDB 表的主键列尽量选用有序的顺序 ID
,而不建议用无序的 ID,比如 UUID、MD5、HASH、字符串列作为主键无法保证数据的顺序增长
3.非聚簇(非聚集)索引
非聚集索引称为二级索引或者辅助索引
3.1.当查找的条件不再是主键的时候:
- 1.上边介绍的聚簇索引只能在搜索条件是主键值时才能发挥作用,因为 B+ 树中的数据都是按照主键进行排序的。那如果想
以其他的列作为搜索条件该怎么办呢
?肯定不能是从头到尾沿着链表依次遍历记录一遍 - 2.答案:
我们可以多建几棵 B+ 树
,不同的 B+ 树中的数据采用不同的排序规则。比方说我们用 c2 列的大小作为数据页、页中记录的排序规则,再建一棵 B+ 树,效果如下图所示
3.2.这个 B+ 树与聚簇索引不同:
- 1.使用记录 c2 列的大小进行记录和页的排序,这包括三个方面的含义:
页内
的记录是按照 c2 列
的大小顺序
排成一个单向链表。- 各个存放用户记录的页也是根据页中记录的 c2 列大小顺序排成一个双向链表。
- 存放目录项记录的页分为不同的层次,在同一层次中的页也是根据页中目录项记录的 c2 列大小顺序排成一个双向链表
- 2.B+ 树的叶子节点存储的并不是完整的用户记录,而
只是 c2 列 + 主键
这两个列的值。 - 3.目录项记录中不再是主键 + 页号的搭配,而变成了
c2 列 + 页号
的搭配。
3.3.举例说明非聚簇索引查找过程:
如果我们现在想通过 c2 列的值查找某些记录的话就可以使用我们刚刚建好的这个 B+ 树了。以查找 c2 列的值为 4 的记录为例,查找过程如下:
- 1.确定目录项记录页。根据根页面,也就是页 44,可以快速定位到目录项记录所在的页为页 42(因为 2 < 4 < 9)。
- 2.通过目录项记录页确定用户记录真实所在的页。在页 42 中可以快速定位到实际存储用户记录的页,但是
由于 c2 列并没有唯一性约束,所以 c2 列值为 4 的记录可能分布在多个数据页中
,又因为 2 < 4 ≤ 4,所以确定实际存储用户记录的页在页 34 和页 35 中。 - 3.在真实存储用户记录的页中定位到具体的记录。到页 34 和页 35 中定位到具体的记录。
- 4.但是这个 B+ 树的叶子节点中的记录只存储了 c2 和 c1(也就是主键)两个列,所以我们
必须再根据主键值去聚簇索引中再查找—遍完整的用户记录
。
3.4.什么是回表:
- 1.我们根据这个以 c2 列大小排序的 B+ 树只能确定我们要查找记录的主键值,所以如果我们想根据 c2 列的值查找到完整的用户记录的话,仍然需要到聚簇索引中再查一遍,这个过程称为
回表
。也就是根据 c2列的值查询一条完整的用户记录需要使用到 2 棵B+ 树
!
3.5.为什么我们还需要一次回表操作呢?
问题:为什么我们还需要一次回表操作呢?直接把完整的用户记录放到叶子节点不 OK 吗?
回答:如果把完整的用户记录放到叶子节点确实是可以不用回表。但是太占地方了,相当于每建立一棵 B+ 树都需要把所有的用户记录再都拷贝一遍,这就有点太浪费存储空间了。
- 1.因为这种按照非主键列建立的 B+ 树需要一次回表操作才可以定位到完整的用户记录,所以这种 B+ 树也被称为二级索引,英文名secondary index,或者辅助索引。
- 2.由于我们使用的是 c2 列的大小作为 B+ 树的排序规则,所以我们也称这个 B+ 树是为 c2 列建立的索引。
- 3.非聚簇索引的存在不影响数据在聚簇索引中的组织,所以
一张表可以有多个非聚簇索引
。
3.6.小结:聚簇索引与非聚簇索引在使用上的区别:
- 1.聚簇索引的
叶子节点
存储的就是我们的数据记录
,非聚簇索引的叶子节点存储的是数据位置
。非聚簇索引不会影响数据表的物理存储顺序 - 2.一个表
只能有一个聚簇索引
,因为只能有一种排序存储的方式,但可以有多个非聚簇索引
,也就是多个索引目录提供数据检索 - 3.使用
聚簇索引
的时候,数据的查询效率高
,但如果对数据进行插入、删除、更新等操作,效率会比非聚簇索引低
4.联合索引
联合 索引就是属于非聚簇索引
4.1.联合索引概念:
- 1.我们也可以同时
以多个列的大小作为排序规则
,也就是同时为多个列建立索引
,比方说我们想让 B+ 树按照 c2 和 c3 列的大小进行排序,这个包含两层含义:- 先把各个记录和页按照 c2 列进行排序。
- 在记录的 c2 列相同的情况下,采用 c3 列进行排序。
4.2.为 c2 和 c3 列建立的索引的示意图如下:
4.3.如图所示,我们需要注意以下几点:
- 1.每条目录项记录都由
c2、c3、页号
这三个部分组成,各条记录先按照 c2 列的值进行排序,如果记录的c2 列相同,则按照 c3 列的值进行排序
。 - 2.B+ 树
叶子节点处
的用户记录由c2、c3 和主键 c1列组成
。
4.4.联合索引本质:
- 1.以 c2 和 c3 列的大小为排序规则建立的 B+ 树称为联合索引,本质上也是一个二级索引。它的意思与分别为 c2 和 c3 列分别建立索引的表述是不同的,不同点如下:
- 建立联合索引只会建立如上图一样的 1 棵 B+ 树。
- 为 c2 和 c3 列分别建立索引会分别以 c2 和 c3 列的大小为排序规则建立 2 棵 B+ 树。
5.InnoDB的B+树索引和MyISAM 索引介绍
5.1.InnoDB的B+树索引的注意事项
a.根页面位置万年不动
- 1.我们前边介绍 B+ 树索引的时候,为了大家理解上的方便,先把存储用户记录的叶子节点都画出来,然后接着画存储目录项记录的内节点,而
实际上 B+ 树的形成过程是这样的
:- 每当为某个表创建一个 B+ 树索引(聚簇索引不是人为创建的,默认就有)的时候,都会为这个索引
创建一个根节点
页面。最开始表中没有数据的时候,每个 B+ 树索引对应的根节点中既没有用户记录也没有目录项记录。 - 随后向表中插入用户记录时,先把用户记录存储到这个根节点中。
- 当根节点中的可用空间用完时继续插入记录,此时会
将根节点中的所有记录复制到一个新分配的页
,比如页 a 中,然后对这个新页进行页分裂的操作
,得到另一个新页,比如页 b。这时新插入的记录根据键值(也就是聚簇索引中的主键值,二级索引中对应的索引列的值)的大小就会被分配到页 a 或者 b 中,而根节点便升级为存储目录项记录的页
- 每当为某个表创建一个 B+ 树索引(聚簇索引不是人为创建的,默认就有)的时候,都会为这个索引
- 2.这个过程特别注意的是:
一个 B+ 树索引的根节点自诞生之日起,便不会再移动
。- 这样只要我们对某个表建立一个索引,那么它的根节点的页号便会被记录到某个地方,然后凡是 InnoDB 存储引擎需要用到这个索引的时候,都会从那个固定的地方取出根节点的页号,从而来访问这个索引
b.内节点中目录项记录的唯一性
- 1.我们知道 B+ 树索引的
内节点
中目录项记录的内容是索引列+页号
的搭配,但是这个搭配对于二级索引来说有点儿不严谨
。还拿index_demo 表为例,假设这个表中的数据是这样的,新插入的数据中,c2列的值都一样
- 2.如果二级索引中目录项记录的内容只是索引列+页号的搭配的话,那么为 c2 列建立索引后的 B+ 树应该长这样:
- 如果我们想新插入一行记录,其中
c1、c2、c3 的值分别是 9、1、 ‘c’
,那么在修改这个为 c2 列建立的二级索引对应的 B+ 树时便碰到了个大问题:由于页 3 中存储的目录项记录是由 c2 列+页号的值构成的,页 3 中的两条目录项记录对应的 c2 列的值都是 1,而我们新插入的这条记录的 c2 列的值也是 1,那我们这条新插入的记录到底应该放到页 4 中,还是应该放到页 5 中啊?答案是:对不起,懵了。 - 3.为了让新插入记录能找到自己在那个页里,我们需要
保证在 B+ 树的同一层内节点的目录项记录除页号这个字段以外是唯一的
。所以对于二级索引的内节点的目录项记录的内容
实际上是由三个部分构成的:索引列的值、主键号、列号
。也就是我们把主键值也添加到二级索引内节点中的目录项记录了,这样就能保证 B+ 树每一层节点中各条目录项记录除页号这个字段外是唯一的,所以我们为 c2 列建立二级索引后的示意图实际上应该是这样子的:
- 4.这样我们再插入记录 (9, 1, ‘c’) 时,由于页 3 中存储的目录项记录是由c2列+主键+页号的值构成的,可以先把新记录的 c2 列的值和页 3 中各目录项记录的 c2 列的值作比较,如果 c2 列的值相同的话,可以接着比较主键值,因为 B+ 树同一层中不同目录项记录的 c2 列+主键的值肯定是不一样的,所以最后肯定能定位唯一的一条目录项记录,在本例中最后确定新记录应该被插入到页 5 中。
c.一个页面最少存储2条记录
- 一个 B+ 树只需要很少的层级就可以轻松存储数亿条记录,查询速度相当不错!这是因为 B+ 树本质上就是一个大的多层级目录,每经过一个目录时都会过滤掉许多无效的子目录,直到最后访问到存储真实数据的目录。
那如果一个大的目录中只存放一个子目录是什么效果呢?那就是目录层级非常非常非常多,而且最后的那个存放真实数据的目录中只能存放一条记录。费了半天劲只能存放一条真实的用户记录
?所以 InnoDB 的一个数据页至少可以存放两条记录。
5.2.MyISAM索引:
- 1.B树索引适用存储引擎如表所示:
- 1.即使多个存储引擎支持同一种类型的索引,但是他们的实现原理也是不同的。Innodb和MyISAM默认的索引是Btree索引;而Memory默认的索引是Hash索引
- 2.MyISAM引擎使用 B+Tree 作为索引结构,叶子节点的data域存放的是 数据记录的地址
a.MyISAM索引的原理:
a1.MyISAM 中没有聚簇索引
- 1.我们知道 InnoDB 中索引即数据,也就是聚簇索引的那棵 B+ 树的叶子节点中已经把所有完整的用户记录都包含了
- 2.在MyISAM 的索引方案虽然也使用树形结构,但是却将索引和数据分开存储,可以理解为 MyISAM 中没有聚簇索引
- 将表中的记录
按照记录的插入顺序单独存储在一个文件中
,称之为数据文件。这个文件并不划分为若干个数据页,有多少记录就往这个文件中塞多少记录就可以了。由于在插入数据的时候并没有刻意按照主键大小排序,所以我们并不能在这些数据上使用二分法进行查找
- 使用 MyISAM 存储引擎的表会把索引信息另外存储到一个称为索引文件的另一个文件中
- MyISAM 会单独为表的主键创建一个索引,只不过在索引的叶子节点中存储的不是完整的用户记录,而是
主键值+数据记录地址的组合
。
- 将表中的记录
- 1.这里设表一共有三列,假设我们以 Col1 为主键,上图是一个 MylSAM 表的主索引 (Primary key) 示意。可以看出
MylSAM 的索引文件仅仅保存数据记录的地址
。在 MyISAM 中,主键索引和二级索引 (Secondary key) 在结构上没有任何区别,只是主键索引要求 key 是唯一的,而二级索引的 key 可以重复。
a2.以 Col2 上建立一个二级索引,则此索引的结构如下图所示:
- 同样也是一棵 B+ 树,data 域保存数据记录的地址。因此,
MyISAM 中索引检索的算法为
:首先按照 B+ 树搜索算法搜索索引,如果指定的Key 存在,则取出其 data 域的值,然后以 data 域的值为地址,读取相应数据记录。
5.3.MyISAM 与 InnoDB 对比
MyISAM 的索引方式都是“非聚簇”的,与 InnoDB 包含 1 个聚簇索引是不同的。
a.小结一下两种引擎中索引的区别:
- 在 InnoDB 存储引擎中,我们只需要根据主键值对聚簇索引进行一次查找就能找到对应的记录,而
在 MyISAM 中却需要进行一次回表操作
,意味着 MyISAM 中建立的索引相当于全部都是二级索引
- InnoDB 的数据文件本身就是索引文件,而 MyISAM 索引文件和数据文件是分离的,索引文件仅保存数据记录的地址。
- InnoDB 的非聚簇索引 data 域存储相应记录主键的值,而 MyISAM 索引记录的是地址 。换句话说,InnoDB 的所有非聚簇索引都引用主键作为 data 域。
- MyISAM 的回表操作是十分快速的,因为是拿着地址偏移量直接到文件中取数据的,反观 InnoDB 是通过获取主键之后再去聚簇索引里找记录,虽然说也不慢,但还是比不上直接用地址去访问。
- InnoDB 要求表必须有主键(MyISAM可以没有)。如果没有显式指定,则 MySQL系统会自动选择一个可以非空且唯一标识数据记录的列作为主键。如果不存在这种列,则 MySQL 自动为 InnoDB 表生成一个隐含字段作为主键,这个字段长度为 6 个字节,类型为长整型。
b.总结:
了解不同存储引擎的索引实现方式对于正确使用和优化索引都非常有帮助
- 举例 1:知道了 InnoDB 的索引实现后,就很容易明白
为什么不建议使用过长的字段作为主键
,因为所有二级索引都引用主键索引,过长的主键索引会令二级索引变得过大 - 举例 2:
用非单调的字段作为主键在 InnoDB 中不是个好主意
,因为 InnoDB 数据文件本身是一棵 B+ 树,非单调的主键会造成在插入新记录时,数据文件为了维持 B+ 树的特性而频繁的分裂调整,十分低效,而使用自增字段作为主键则是一个很好的选择。
6.索引的代价
索引是个好东西,但可不能乱建,它在空间和时间上都会有消耗:
6.1.空间上的代价
- 1.每建立一个索引都要为它建立一棵 B+ 树,每一棵 B+ 树的每一个节点都是一个数据页,一个页默认会占用 16KB 的存储空间,一棵很大的 B+ 树由许多数据页组成,那就是很大的一片存储空间。
6.2.时间上的代价
- 1.每次对表中的数据进行
增、删、改
操作时,都需要去修改各个 B+ 树索引。而且我们讲过,B+ 树每层节点都是按照索引列的值从小到大的顺序排序,而组成了双向链表。不论是叶子节点中的记录,还是内节点中的记录(也就是不论是用户记录还是目录项记录)都是按照索引列的值从小到大的顺序而形成了一个单向链表。而增、删、改操作可能会对节点和记录的排序造成破坏
,所以存储引擎需要额外的时间进行一些 记录移位、页面分裂、页面回收等操作来维护好节点和记录的排序。如果我们建了许多索引,每个索引对应的 B+ 树都要进行相关的维护操作,会给性能拖后腿。
一个表上索引建的越多,所占用存储空间就越多,在增删改记录的时候性能就越差。为了能建立又好又少的索引,要了解这些索引在哪些条件下起作用。