MySQL浓缩笔记(2)-索引篇

 文章笔记主要引用:

阿秀的学习笔记 (interviewguide.cn)

小林coding (xiaolincoding.com)

索引如何提高查询速度的

将无序的数据变成相对有序的数据(就像查有目的一样)

为什么使用索引?

  • 通过创建唯一性索引,可以保证数据库表中每一行数据的唯一性
  • 可以大大加快数据的检索速度,这也是创建索引的最主要的原因。
  • 帮助服务器避免排序和临时表
  • 随机IO变为顺序IO
  • 可以加速表和表之间的连接,特别是在实现数据的参考完整性方面特别有意义。

Innodb为什么要用自增id作为主键?

如果表使用自增主键,那么每次插入新的记录,记录就会顺序添加到当前索引节点的后续位置,当一页写满,就会自动开辟一个新的页。 如果使用非自增主键(如果身份证号或学号等),由于每次插入主键的值近似于随机,因此每次新纪录都要被插到现有索引页得中间某个位置, 频繁的移动、分页操作造成了大量的碎片,得到了不够紧凑的索引结构,后续不得不通过OPTIMIZE TABLE(optimize table)来重建表并优化填充页面。

索引分类

我们可以按照四个角度来分类索引。

  • 按「数据结构」分类:B+tree索引、Hash索引、Full-text索引

  • 按「物理存储」分类:聚簇索引(主键索引)、二级索引(辅助索引)

  • 按「字段特性」分类:主键索引、唯一索引、普通索引、前缀索引

  • 按「字段个数」分类:单列索引、联合索引

MySQL中有哪些索引?有什么特点?

  • 普通索引:仅加速查询
  • 唯一索引:加速查询 + 列值唯一(可以有null)
  • 主键索引:加速查询 + 列值唯一(不可以有null)+ 表中只有一个
  • 组合索引:多列值组成一个索引,专门用于组合搜索,其效率大于索引合并
  • 全文索引:对文本的内容进行分词,进行搜索
  • 索引合并:使用多个单列索引组合搜索
  • 覆盖索引:select的数据列只用从索引中就能够取得,不必读取数据行,换句话说查询列要被所建的索引覆盖
  • 聚簇索引:表数据是和主键一起存储的,主键索引的叶结点存储行数据(包含了主键值),二级索引的叶结点存储行的主键值。使用的是B+树作为索引的存储结构,非叶子节点都是索引关键字,但非叶子节点中的关键字中不存储对应记录的具体内容或内容地址。叶子节点上的数据是主键与具体记录(数据内容)

MySQL中有四种索引类型,可以简单说说吗?

  • FULLTEXT :即为全文索引,目前只有MyISAM引擎支持。其可以在CREATE TABLE ,ALTER TABLE ,CREATE INDEX 使用,不过目前只有 CHAR、VARCHAR ,TEXT 列上可以创建全文索引,需要注意的是MySQL5.6以后支持全文索引了,5.6之前是不支持的。
  • HASH :由于HASH的几乎唯一(几乎100%的唯一)及类似键值对的形式,很适合作为索引。 HASH索引可以一次定位,不需要像树形索引那样逐层查找,因此具有极高的效率。但是,这种高效是有条件的,即只在“=”和“in”条件下高效,对于范围查询、排序及组合索引仍然效率不高。
  • BTREE :BTREE索引就是一种将索引值存入一个树形的数据结构中。这是MySQL里默认和最常用的索引类型。
  • RTREE :RTREE在MySQL很少使用,仅支持geometry数据类型,支持该类型的存储引擎只有MyISAM、BDb、InnoDb、NDb、Archive几种。 相对于BTREE,RTREE的优势在于范围查找。

MySQL索引主要使用的两种数据结构是什么?

  • 哈希索引,对于哈希索引来说,底层的数据结构肯定是哈希表,因此在绝大多数需求为单条记录查询的时候,可以选择哈希索引,查询性能最快;其余大部分场景,建议选择BTree索引

  • BTree索引,Mysql的BTree索引使用的是B树中的B+Tree,BTREE索引就是一种将索引值按一定的算法,存入一个树形的数据结构中(二叉树),每次查询都是从树的入口root开始,依次遍历node,获取leaf。

    但对于主要的两种存储引擎(MyISAM和InnoDB)的实现方式是不同的。

什么是聚集(聚簇)索引 ?

正文内容本身就是一种按照一定规则排列的目录称为"聚集索引",索引和数据行在一起,叶子节点就是数据节点

聚集索引就是按照拼音查询。

其实,我们的汉语字典的正文本身就是一个聚集索引。比如,我们要查"安"字,就会很自然地翻开字典的前几页,因为"安"的拼音是"an",而按照拼音排序 汉字的字典是以英文字母"a"开头并以"z"结尾的,那么"安"字就自然地排在字典的前部。如果您翻完了所有以"a"开头的部分仍然找不到这个字,那么就 说明您的字典中没有这个字;同样的,如果查"张"字,那您也会将您的字典翻到最后部分,因为"张"的拼音是"zhang"。也就是说,字典的正文部分本身 就是一个目录,您不需要再去查其他目录来找到您需要找的内容。

#34、什么是非聚合索引?

目录纯粹是目录,正文纯粹是正文的排序方式称为"非聚集索引",如果叶子节点没有存储数据行,那么就是非聚簇索引。

非聚集索引就是按照偏旁等来进行查询。

如果您认识某个字,您可以快速地从自动中查到这个字。但您也可能会遇到您不认识的字,不知道它的发音,这时候,您就不能按照刚才的方法找到您要查的字,而 需要去根据"偏旁部首"查到您要找的字,然后根据这个字后的页码直接翻到某页来找到您要找的字。但您结合"部首目录"和"检字表"而查到的字的排序并不是 真正的正文的排序方法,比如您查"张"字,我们可以看到在查部首之后的检字表中"张"的页码是672页,检字表中"张"的上面是"驰"字,但页码却是63 页,"张"的下面是"弩"字,页面是390页。很显然,这些字并不是真正的分别位于"张"字的上下方,现在您看到的连续的"驰、张、弩"三字实际上就是他 们在非聚集索引中的排序,是字典正文中的字在非聚集索引中的映射。我们可以通过这种方式来找到您所需要的字,但它需要两个过程,先找到目录中的结果,然后 再翻到您所需要的页码。

聚集索引与非聚集索引的区别是什么?

聚集索引和非聚集索引的区别在于, 通过聚集索引可以查到需要查找的数据, 而通过非聚集索引可以查到记录对应的主键值 , 再使用主键的值通过聚集索引查找到需要的数据。 聚集索引和非聚集索引的根本区别是表记录的排列顺序和与索引的排列顺序是否一致

聚集索引(Innodb)的叶节点就是数据节点,而非聚集索引(MyISAM)的叶节点仍然是索引节点,只不过其包含一个指向对应数据块的指针。

索引缺点(既然索引有那么多优点,为什么不对表总的每一列创建一个索引呢?):

  • 需要占用物理空间,数量越大,占用空间越大;

  • 创建索引和维护索引要耗费时间,这种时间随着数据量的增加而增大;

  • 会降低表的增删改的效率,因为每次增删改索引,B+ 树为了维护索引有序性,都需要进行动态维护。

什么时候需要建立数据库索引呢?

  • 字段有唯一性限制的,比如商品编码;

  • 经常用于 WHERE 查询条件的字段,这样能够提高整个表的查询速度,如果查询条件不是一个字段,可以建立联合索引。

  • 经常用于 GROUP BYORDER BY 的字段,这样的用来查询和分组的字段,这样在查询的时候就不需要再去做一次排序了,因为我们都已经知道了建立索引之后在 B+Tree 中的记录都是排序好的。

什么时候不需要建立数据库索引呢?

  • WHERE 条件,GROUP BYORDER BY 里用不到的字段,索引的价值是快速定位,如果起不到定位的字段通常是不需要创建索引的,因为索引是会占用物理空间的。

  • 字段中存在大量重复数据,不需要创建索引,比如性别字段,只有男女,如果数据库表中,男女的记录分布均匀,那么无论搜索哪个值都可能得到一半的数据。在这些情况下,还不如不要索引,因为 MySQL 还有一个查询优化器,查询优化器发现某个值出现在表的数据行中的百分比很高的时候,它一般会忽略索引,进行全表扫描。

  • 表数据太少的时候,不需要创建索引;

  • 经常更新的字段不用创建索引,比如不要对电商项目的用户余额建立索引,因为索引字段频繁修改,由于要维护 B+Tree的有序性,那么就需要频繁的重建索引,这个过程是会影响数据库性能的。

索引覆盖和回表

索引覆盖:一个索引包含(或者说覆盖)所有需要查询的字段的值

如果某个查询语句使用了二级索引,但是查询的数据不是主键值,这时在二级索引找到主键值后,需要去聚簇索引中获得数据行,这个过程就叫作「回表」,也就是说要查两个 B+ 树才能查到数据。不过,当查询的数据是主键值时,因为只在二级索引就能查询到,不用再去聚簇索引查,这个过程就叫作「索引覆盖」,也就是只需要查一个 B+ 树就能找到数据

索引优化(创建索引时需要注意什么?)

  • 前缀索引优化:使用某个字段中字符串的前几个字符建立索引,为了减小索引字段大小有效提高索引的查询速度

  • 覆盖索引优化:二级索引中查询得到记录,而不需要通过聚簇索引查询获得,可以避免回表的操作。假设我们只需要查询商品的名称、价格,建立一个联合索引,即「商品ID、名称、价格」作为一个联合索引。如果索引中存在这些数据,查询将不会再次检索主键索引,从而避免回表

  • 主键索引最好是自增的:插入一条新记录,都是追加操作,不需要重新移动数据,因此这种插入数据的方法效率非常高。

  • 索引最好设置为 NOT NULL:索引列存在 NULL 就会导致优化器在做索引选择的时候更加复杂,更加难以优化NULL 值是一个没意义的值,但是它会占用物理空间

  • 防止索引失效;左或者左右模糊匹配对索引列做了计算、函数、类型转换操;联合索引要能正确使用需要遵循最左匹配原则 ; WHERE 子句中,如果在 OR 前的条件列索引列,而在 OR 后的条件列不是索引列,那么索引会失效。

MySQL 索引使用的注意事项(防止索引失效)

  • 当我们使用左或者左右模糊匹配的时候,也就是 like %xx 或者 like %xx%这两种方式都会造成索引失效;

  • 当我们在查询条件中对索引列使用函数,就会导致索引失效。

  • 当我们在查询条件中对索引列进行表达式计算,也是无法走索引的。

  • MySQL 在遇到字符串和数字比较的时候,会自动把字符串转为数字,然后再进行比较。如果字符串是索引列,而条件语句中的输入参数是数字的话,那么索引列会发生隐式类型转换,由于隐式类型转换是通过 CAST 函数实现的,等同于对索引列使用了函数,所以就会导致索引失效

  • 联合索引要能正确使用需要遵循最左匹配原则,也就是按照最左优先的方式进行索引的匹配,否则就会导致索引失效。

  • 在 WHERE 子句中,如果在 OR 前的条件列是索引列,而在 OR 后的条件列不是索引列,那么索引会失效。

使用索引的注意事项

  • 在经常需要搜索的列上,可以加快搜索的速度;

  • 在经常使用在where子句中的列上面创建索引,加快条件的判断速度。

  • 将打算加索引的列设置为NOT NULL,否则将导致引擎放弃使用索引而进行全表扫描

  • 在经常需要排序的列上创建索引,因为索引已经排序,这样查询可以利用索引的排序,加快排序查询时间

  • 避免where子句中对字段施加函数,这会造成无法命中索引

  • 在中到大型表索引都是非常有效的,但是特大型表的维护开销会很大,不适合建索引,建立用逻辑索引

  • 在经常用到连续的列上,这些列主要是由一些外键,可以加快连接的速度

  • 与业务无关时多使用逻辑主键,也就是自增主键在使用InnoDB时使用与业务无关的自增主键作为主键,即使用逻辑主键,而不要使用业务主键。

  • 删除长期未使用的索引,不用的索引的存在会造成不必要的性能损耗

  • 在使用limit offset查询缓存时,可以借助索引来提高性能。

MyISAM和InnoDB实现B树索引方式的区别是什么?

  • MyISAM,索引文件和数据文件是分离的,B+Tree叶节点的data域存放的是数据记录的地址,在索引检索的时候,首先按照B+Tree搜索算法搜索索引,如果指定的key存在,则取出其data域的值,然后以data域的值为地址读取相应的数据记录,这被称为“非聚簇索引

  • InnoDB,其数据文件本身就是索引文件。其表数据文件本身就是按B+Tree组织的一个索引结构,树的叶子节点data域保存了完整的数据,这个索引的key是数据表的主键,因此InnoDB表数据文件本身就是主索引,这被称为“聚簇索引”或者聚集索引,而其余的索引都作为辅助索引,辅助索引的data域存储相应记录主键的值而不是地址,这也是和MyISAM不同的地方。

    在根据主索引搜索时,直接找到key所在的节点即可取出数据;在根据辅助索引查找时,则需要先取出主键的值,再走一遍主索引。因此,在设计表的时候,不建议使用过长的字段为主键,也不建议使用非单调的字段作为主键,这样会造成主索引频繁分裂。

文件索引和数据库索引为什么使用B+树?(第9个问题的详细回答)

所谓索引,则为了数据的快速定位与查找,那么索引的结构组织要尽量减少查找过程中磁盘I/O的存取次数,因此B+树相比B树更为合适。数据库系统巧妙利用了局部性原理与磁盘预读原理,将一个节点的大小设为等于一个页,这样每个节点只需要一次I/O就可以完全载入,而红黑树这种结构,高度明显要深的多,并且由于逻辑上很近的节点(父子)物理上可能很远,无法利用局部性。

方便扫描数据库:B树必须用中序遍历的方法按序扫描数据库,而B+树直接扫描叶子结点,B+树支持范围查找非常方便,而B树不支持,这是数据库选用B+树的最主要原因。

B+树查找效率更加稳定,B树有可能在中间节点找到数据,稳定性不够。

B+tree的磁盘读写代价更低:B+tree只有叶子节点存放数据。如果把所有非叶子结点的关键字存放在同一块盘中,一次性读入内存中的需要查找的关键字也就越多,相对来说IO读写次数也就降低了;

B+tree的查询效率更加稳定只有叶子节点存放数据,任何关键字的查找必须走一条从根结点到叶子结点的路。所有关键字查询的路径长度相同,导致每一个数据的查询效率相当;

增加B+树的路数可以降低树的高度,那么无限增加树的路数是不是可以有最优的查找效率?

不可以。因为这样会形成一个有序数组,文件系统和数据库的索引都是存在硬盘上的,并且如果数据量大的话,不一定能一次性加载到内存中。有序数组没法一次性加载进内存,这时候B+树的多路存储威力就出来了,可以每次加载B+树的一个结点,然后一步步往下找,

为什么MySQL索引适用用B+树而不用hash表和B树?

  • 利用Hash需要把数据全部加载到内存中,如果数据量大,是一件很消耗内存的事,而采用B+树,是基于按照节点分段加载,由此减少内存消耗
  • 和业务场景有段,对于唯一查找(查找一个值),Hash确实更快,但数据库中经常查询多条数据,这时候由于B+数据的有序性,与叶子节点又有链表相连,他的查询效率会比Hash快的多。
  • b+树的非叶子节点不保存数据只保存子树的临界值(最大或者最小),所以同样大小的节点,b+树相对于b树能够有更多的分支,使得这棵树更加矮胖,查询时做的IO操作次数也更少

InnoDB 是如何存储数据

InnoDB 的数据是按「数据页」为单位来读写的InnoDB 数据页的默认大小是 16KB。数据页中有一个页目录,起到记录索引的作用。

页目录创建的过程如下:

  1. 将所有的记录划分成几个组,这些记录包括最小记录和最大记录,但不包括标记为“已删除”的记录;
  2. 每个记录组的最后一条记录就是组内最大的那条记录,并且最后一条记录的头信息中会存储该组一共有多少条记录,作为 n_owned 字段(上图中粉红色字段)
  3. 页目录用来存储每组最后一条记录的地址偏移量,这些地址偏移量会按照先后顺序存储起来,每组的地址偏移量也被称之为槽(slot),每个槽相当于指针指向了不同组的最后一个记录

从图可以看到,页目录就是由多个槽组成的,槽相当于分组记录的索引。然后,因为记录是按照「主键值」从小到大排序的,所以我们通过槽查找记录时,可以使用二分法快速定位要查询的记录在哪个槽(哪个记录分组),定位到槽后,再遍历槽内的所有记录,找到对应的记录,无需从最小记录开始遍历整个页中的记录链表。

猜你喜欢

转载自blog.csdn.net/shisniend/article/details/131869861