其他分享
首页 > 其他分享> > 索引

索引

作者:互联网

十、索引

1 介绍

索引(Index)是帮助MySQL高效获取数据的数据结构——索引是一种数据结构,是数据库管理系统中一个排序的数据结构,以协助快速查询、更新数据库表中数据。索引的实现通常使用B树及其变种B+树。

索引是一种特殊的文件(InnoDB数据表上的索引是表空间的一个组成部分),它们包含着对数据表里所有记录的引用指针。一般来说索引本身占用内存空间也很大,不可能全部存储在内存中,因此索引往往以文件形式存储在硬盘上,占据一定的物理空间。

更通俗的说,索引就相当于目录。为了方便查找书中的内容,通过对内容建立索引形成目录。可以简单理解为排好序的快速查找数据结构",即索引 = 排序 + 查找,建立索引之后,查找起来会变快。

2 建立索引

2.1 单值索引

假设有如下查询语句经常被使用:

SELECT * FROM user WHERE NAME = 'xxx';

可以通过建立索引,使得杂乱的数据变得有序、易查询。

CREATE INDEX idx_user_name ON USER (name);
# idx_user_name:约定俗成的命名规范,idx表示这是一个索引,user表示在user表建立索引,name是建立索引的字段

2.2 复合索引

复合索引就是给多个字段建立索引:

CREATE INDEX idx_user_name ON USER (name,email);

3 索引的优缺点

索引的优点:

索引的缺点:

4 索引分类

主键索引:数据列不允许重复,不允许为NULL,一个表只能有一个主键。

唯一索引:数据列不允许重复,允许为NULL值,一个表允许多个列创建唯一索引。

普通索引:基本的索引类型,没有唯一性的限制,即一个索引只包含单个列,允许为NULL值。一个表可以有多个单列索引;建议一张表索引不要超过5个。

全文索引: 是目前搜索引擎使用的一种关键技术。

5 索引语法

5.1 建立索引

CREATE [UNIQUE] INDEX  indexName ON mytable(columnname(length));
# 或者
ALTER mytable ADD [UNIQUE]  INDEX [indexName] ON(columnname(length));

# 如果是CHAR和VARCHAR类型,length可以小于字段实际长度;如果是BLOB和TEXT类型,必须指定length。

5.2 删除索引

DROP INDEX [indexName] ON mytable;

5.3 查看索引

\G表示将查询到的横向表格纵向输出,方便阅读

SHOW INDEX FROM table_name\G

6 索引的数据结构

索引的数据结构和具体存储引擎的实现有关,在MySQL中使用较多的索引有Hash索引,B+树索引等,而我们经常使用的InnoDB存储引擎的默认索引实现为:B+树索引。对于哈希索引来说,底层的数据结构就是哈希表,因此在绝大多数需求为单条记录查询的时候,可以选择哈希索引,查询性能最快;其余大部分场景,建议选择BTree索引。

6.1 BTree索引

B树索引是Mysql数据库中使用最频繁的索引类型,基本所有存储引擎都支持BTree索引。通常我们说的索引不出意外指的就是(B树)索引。

如下图的一颗 B 树, 浅蓝色的块我们称之为一个磁盘块, 可以看到每个磁盘块包含几个数据项(深蓝色所示) 和指针(黄色所示)。比如磁盘块 1 包含数据项 17 和 35, 包含指针 P1、 P2、 P3。P1 表示小于 17 的磁盘块, P2 表示在 17 和 35 之间的磁盘块, P3 表示大于 35 的磁盘块,以此类推。

如果要查找数据项 29, 那么首先会把磁盘块 1 由磁盘加载到内存, 此时发生一次 IO, 在内存中用二分查找确定 29在 17 和 35 之间, 锁定磁盘块 1 的 P2 指针, 内存时间因为非常短(相比磁盘的 IO) 可以忽略不计
通过磁盘块 1的 P2 指针的磁盘地址把磁盘块 3 由磁盘加载到内存, 发生第二次 IO, 29 在 26 和 30 之间, 锁定磁盘块 3 的 P2 指针
通过指针加载磁盘块 8 到内存, 发生第三次 IO, 同时内存中做二分查找找到 29, 结束查询, 总计三次 IO。
image

6.2 B+Tree索引

B-树的关键字(数据项)和记录是放在一起的; B+树的非叶子节点中只有关键字和指向下一个节点的索引, 记录只放在叶子节点中。

在 B 树中, 越靠近根节点的记录查找时间越快, 只要找到关键字即可确定记录的存在; 而 B+ 树中每个记录的查找时间基本是一样的, 都需要从根节点走到叶子节点, 而且在叶子节点中还要再比较关键字。从这个角度看 B 树的性能好像要比 B+ 树好, 而在实际应用中却是 B+ 树的性能要好些。 因为 B+ 树的非叶子节点不存放实际的数据,这样每个节点可容纳的元素个数比 B 树多, 树高比 B 树小, 这样带来的好处是减少磁盘访问次数。

尽管 B+ 树找到一个记录所需的比较次数要比 B 树多, 但是一次磁盘访问的时间相当于成百上千次内存比较的时间, 因此实际中B+ 树的性能可能还会好些, 而且 B+树的叶子节点使用指针连接在一起, 方便顺序遍历(范围搜索), 这也是很多数据库和文件系统使用 B+树的缘故。

下图是一个B+树的示例:

image

B+tree性质:

总的来说,B+树查找的效率要比B树更高、更稳定。由于非叶子节点并不是最终指向文件内容的节点, 而只是叶子节点中关键字的索引, 所以任何关键字的查找必须走一条从根节点到叶子节点的路。 所有关键字查询的路径长度相同,使得每一个数据的查询效率相当。

6.3 Hash索引

类似于数据结构中简单实现的HASH表(散列表)一样,当我们在mysql中用哈希索引时,主要就是通过Hash算法(常见的Hash算法有直接定址法、平方取中法、折叠法、除数取余法、随机数法),将数据库字段数据转换成定长的Hash值,与这条数据的行指针一并存入Hash表的对应位置;如果发生Hash冲突(两个不同关键字的Hash值相同),则在对应Hash键下以链表形式存储(拉链法),如图:

image

7 何时需要建索引

7.1 适合建立索引的情况

7.2 不适合建立索引的情况

7.3 总结

索引的创建最好符合以下原则:

另外,索引的选择性是指索引列中不同值的数目与表中记录数的比。如果一个表中有2000条记录,表索引列有1980个不同的值,那么这个索引的选择性就是1980/2000=0.99。一个索引的选择性越接近于1,这个索引的效率就越高。

8 聚簇索引、回表

8.1 基本概念

聚簇索引:将数据与索引存放到一起,找到索引就可以直接找到数据。

非聚簇索引:将数据存储与索引分开,索引结构的叶子节点存储的是对应数据的地址,并不能直接获取到数据。

回表:举个例子,非聚簇索引中找到了叶子节点,获取叶子节点存储的地址,根据这个地址再去查找一次,这个过程叫做回表。

下图为一个聚簇索引:

image

一个表建立后,如果有主键,主键就是默认的聚簇索引。如果表中没有定义主键,InnoDB会选择一个唯一的非空索引代替。如果没有这样的索引,InnoDB会隐式定义一个主键来作为聚簇索引。

由于存储引擎负责实现索引,因此不是所有的存储引擎都支持聚簇索引(比如MyISAM不支持聚簇索引),MyISAM使用非聚簇索引,如下图:
image

需要注意:

8.2 聚簇索引和费聚簇索引的区别

image

InnoDB使用的是聚簇索引,将主键组织到一棵B+树中,而行数据就储存在叶子节点上,若使用"where id = 14" 这样的条件查找主键,则按照B+树的检索算法即可查找到对应的叶节点,之后获得行数据。

若对Name列进行条件搜索,则需要两个步骤:第一步在辅助索引B+树中检索Name,到达其叶子节点获取对应的主键。第二步使用主键在主索引B+树种再执行一次B+树检索操作,最终到达叶子节点即可获取整行数据。(重点在于通过其他键需要建立辅助索引)

聚簇索引优点:

聚簇索引缺点:

下图展示了使用uuid主键和自增主键的区别:
image
image

8.3 非聚簇索引与回表

非聚簇索引不一定进行回表查询。

这涉及到查询语句所要求的字段是否全部命中了索引,如果全部命中了索引,那么就不必再进行回表查询。

举个简单的例子,假设我们在员工表的年龄上建立了索引,那么当进行SELECT age FROM employee WHERE age < 20的查询时,在索引的叶子节点上,已经包含了age信息,就不会再次进行回表查询。

8.4 何时使用聚簇索引与非聚簇索引

image

9 MyISAM索引与InnoDB索引的区别

覆盖索引:

如果要查询的字段都建立过索引,那么引擎会直接在索引表中查询而不会访问原始数据(否则只要有一个字段没有建立索引就会做全表扫描),这叫索引覆盖。因此我们需要尽可能的在select后只写必要的查询字段,以增加索引覆盖的几率。

这里值得注意的是不要想着为每个字段建立索引,因为优先使用索引的优势就在于其体积小。

10 联合索引

MySQL可以使用多个字段同时建立一个索引,叫做联合索引。在联合索引中,如果想要命中索引,需要按照建立索引时的字段顺序挨个使用,否则无法命中索引。
image
如图,我们在三个列上建立了一个联合索引:整形id、字符串name、日期birthDate。那么索引的排序为:先按照id 排序,如果id 相同,则按照name排序,如果name的值也相等,则按照birthDate进行排序。

当进行查询时,此时索引仅仅按照id严格有序,因此必须首先使用id字段进行等值查询,之后对于匹配到的列而言,其按照name字段严格有序,此时可以使用name字段用做索引查找,以此类推。如果直接按照name查找: select* from employee where name='Staff' 就不会走索引,因为name是依靠着id进行排序的。

因此,在建立联合索引的时候应该注意索引列的顺序,一般情况下,将查询需求频繁或者字段选择性高的列放在前面。此外可以根据特例的查询或者表结构进行单独的调整。

标签:name,聚簇,查询,索引,主键,节点
来源: https://www.cnblogs.com/yyyzyyyz/p/15848644.html