【MySQL笔记】正确的理解MySQL的索引机制以及内部实现(二)

正确的理解MySQL的索引机制以及内部实现(二)


如果觉得对你有帮助,能否点个赞或关个注,以示鼓励笔者呢?!博客目录 | 先点这里

  • 第一部分 倾向于MySQL数据库索引的日常生活,主要体现MySQL索引的应用
    正确的理解MySQL的索引机制以及内部实现(一)
  • 第二部分 更倾向于讲解MySQL B+树索引的实现原理
    正确的理解MySQL的索引机制以及内部实现(二)

因为数据库索引的知识点比较多,而且感觉比较复杂和混乱!所以为了让文章更加的清晰,最终在按原目录结构写了三分之二的时候,还是决定分为两个部分分开去描述(虽然还是有很多地方没有去解释)


  • 前提概要
    • 基础数据结构
    • B树和B+树
  • 索引的一些概念
    • 主键索引和辅助键索引
    • 聚簇索引与非聚簇索引
    • 稠密索引和稀疏索引
    • 覆盖索引
  • InnoDB和MyISAM下的索引
    • InnoDB下的B+Tree索引
    • MyISAM下的B+Tree索引
    • 物理空间角度理解不同引擎下的数据存储
    • InnoDB、MyISAM下索引实现的区别和优缺点
    • 关于聚簇索引容易混淆的小贴士
  • 索引的其他底层实现
    • 哈希索引和BitMap索引
    • 倒排索引
  • 索引名词混乱的现状
    • 对于索引名词混乱的个人感受
    • 容易混淆的索引名词概念

前提概要


基础数据结构

在学习MySQL的索引之前,我们很有必要先知道一个数据结构知识。

  • 二分搜索树
    初入数据结构的二叉搜索树以及Java实现
  • 平衡二叉树
    初入数据结构中的平衡二叉搜索树(AVL树)及Java实现
  • B树,B+树
    初入数据结构中的B类树(B Tree , B+ Tree)

可以说,如果不知道这三个数据结构的情况下,硬啃MySQL索引是很吃力的,也不好理解。

因为一篇博客的篇幅有限,我也不想写成两三万字的大长文,所以有需要的朋友,可以从上面的子链看看这三个数据结点的简介和概念再回到本文继续浏览


B树,B+树

毕竟B类树是构造MySQL数据库索引的底层实现(准确的说是B+树),所以本文还是要略带的介绍一下的。

什么是B树?

B树,又名B-树(B减树)。它是一棵多路平衡多路查找树。它具有一些索引结构的特性

  • 一个结点可以存储多个数据
  • 一个结点可以有多个孩子
  • 自平衡,所有叶子结点都处于同一个层次

总之,使用B树作为索引的存储结构,可以大幅的降低树的高度,保持一棵"矮胖树"的特性, 降低磁盘IO操作,提高数据库查询性能
【MySQL笔记】正确的理解MySQL的索引机制以及内部实现(二)_第1张图片
什么是B+树?

那什么是一棵B+树呢?B+树说白了就是B树的变种,也是一棵平衡多路查找树,但分别结合了B树和索引顺序访问的优势,更适合成为数据库索引的底层实现

  • 所有非叶子结点只作为中间结点,存放指针地址,不存放直接的数据,所有数据存放在叶子结点上
  • 叶子结点中的所有数据都带有指向上一个,下一个数据的指针,实际双向链表结构,可以实现双向顺序访问

总而言之就是,B+树综合了B树的优点,还带有顺序索引的功能,更加的牛逼
【MySQL笔记】正确的理解MySQL的索引机制以及内部实现(二)_第2张图片

B+树为什么替换了B树称为MySQL索引的底层实现?

  • B+树的磁盘读写代价更低
    因为B+树的中间结点存储的都是索引数据,仅仅是一个地址,并非直接的数据,所以同一个结点中(同一个磁盘页大小),B+树可容纳的关键字数会比B树更多(因为一个简单的地址几乎肯定小于一个直接的数据)。所以同样的数据量下,B+树会比B树更加“矮胖”,树高更小,所以查询时需要的IO次数就更少
  • B+树的查询效率更加稳定
    因为B+树的所有元素都存储在叶子结点中,而叶子结点都属于同一层级,每一个B+树查询都是从根结点遍历到叶子结点的过程,所以不管查询什么,时间复杂度相比B树查询都更加的稳定和近似。
  • B+树更有利对数据的扫描
    B树中虽然解决了查询的效率,但是如果需要查询一串相邻的数值,有可能需要回溯来回扫描或是从根结点多次中序遍历。而B+树的所有元素都存储叶子结点,每个叶子结点都有指向下一个结点的指针,直接线性遍历即可。同样B+树也更加的利于做范围查询

如果想要知道数据库索引的底层实现是怎么从二叉搜索树->平衡二叉搜索树->B树->B+树的演进过程的也可以看这篇文章初入数据结构中的B类树(B Tree , B+ Tree)


索引的一些概念


主键索引和辅助键索引

(一)主键索引(Primary Index)

  • 主键索引(primary index)
    主键索引又称主索引
    以表的主键(primary key)创建的索引树,我们就称其为该表的主键索引

(二)辅助键索引(Secondary Index)

  • 辅助键索引(secondary index)
    辅助键索引,又称辅助索引次级索引二级索引
    辅助索引以表的非主键的字段创建的索引树,我们就称其为该表的辅助键索引

简而言之,如果将一张表的所有字段都单独建立一个索引,那么除了以主键生成的索引外,其他都属于辅助键索引。 其实辅助键索引更常叫辅助索引,只是我为了对应主键索引,还是叫辅助键索引能更让人容易理解,不容易混乱。


聚簇索引和非聚簇索引

聚簇 - @百度百科

  • 聚簇是为了提高某个属性(或属性组)的查询速度,把这个或这些属性(称为聚簇码)上具有相同值的元组集中存放在连续的物理块。

(一)聚簇索引(Clustered Index)

什么是聚簇索引?

  • 聚簇索引又称聚集索引。我们将根据键值对数据库表中的数据进行排序存储,并将相关的信息聚簇在一起索引就叫聚簇索引(该顺序是物理上连续的存储空间的顺序)
  • 针对MySQL而言,聚簇索引只存在于InnoDB中,再具体些,如果有主键,一般指代的是InnoDB每个表的主键索引

聚簇索引的特性

  • 聚簇索引定义了数据存储在表中的顺序,该表的数据有且仅以一种方式排序。因此,每个表只能有一个聚簇索引。在RDBMS中,在存在主键的情况下,主键索引就是该聚簇索引

  • 聚簇索引其中一个大特征就是将索引和数据存储在同一个文件中,既叶子结点不仅保存键的信息,还保存了位于同一行其他列的信息,简而言之,聚簇索引的叶子结点保存的是一个完整行记录数据

  • 同时我们也能知道聚簇索引是一种有序索引,它的具体实现可以是稠密索引,也可以是稀疏索引

【MySQL笔记】正确的理解MySQL的索引机制以及内部实现(二)_第3张图片
那些索引属于聚簇索引?

  • InnoDB和MyISAM之间,只有InnoDB支持聚簇索引
  • 若一张表存在主键,则以该主键列生聚簇索引树
  • 若一张表没有主键,则MySQL会找到该表的第一个唯一非空列的索引作为聚簇索引
  • 如以上条件皆不满足,InnoDB会在内部生成一个名为GEN_CLUST_INDEX隐式聚簇索引。该索引是基于一个名为DB_ROW_ID的隐藏字段,通常称之隐式主键。

为什么InnoDB存储引擎一定要有聚簇索引呢?

  • 因为如果我们的SQL条件是一个非主键列的数据,那么在底层索引查询中,很可能需要跨树查询,既两次查询。
  • 既InnoDB的辅助键索引的叶子结点并不存储行数据,而条件值对应的该主键值。然后我们需要根据辅助键索引查询到的主键值,再去聚簇索引中查询所以非主键索引包含两次查找,一次是查找次级索引自身,然后能再查找主键

(二)非聚簇索引(Non- Clustered Index)

什么是非聚簇索引?

  • 非聚簇索引将数据存储在一个位置,将索引存储在另一个位置,索引包含指向该数据位置的指针。这样的一个索引就是非聚簇索引,一个表中可以包含多个非聚簇索引

非聚簇索引的特征

  • 非聚簇索引是一个与聚簇索引想向的概念。针对MySQL而言,可以说InnoDB的辅助键索引,以及MyISAM的主、辅索引都是非聚簇索引
  • 非聚簇索引只存储键与指针,不存储数据,所以非聚簇索引的叶子结点仅保存数据的地址(MyISAM)或是其主键信息(InnoDB)。
  • 使用非聚簇索引进行查询,最终会定位到叶子结点,得到数据地址或主键信息,。然后还要根据获得的地址或主键信息进一步定位到数据,通常作为中间人的作用。
    【MySQL笔记】正确的理解MySQL的索引机制以及内部实现(二)_第4张图片

那些索引属于非聚簇索引

  • 说白了,InnoDB下的辅助键索引和MyISAM下的主、辅键索引都属于非聚簇索引,仅仅只有InnoDB下的主键索引(唯一非空列索引…之后细节忽视)才属于聚簇索引

稠密索引和稀疏索引

引用至:
@difference between sparse index and dense index - @stackoverflow
@Indexing in Databases | Set 1 - @GeeksForGeeks

Ordered Indexing is of two types:

  • Dense Index
  • Sparse Index

.
Dense Index :

  • An index record appears for every search key value in file.

  • This record contains search key value and a pointer to the actual record.

.

Sparse Index :

  • Index records are created only for some of the records.
  • To locate a record, we find the index record with the largest search key value less than or equal to the search key value we are looking for.
  • We start at that record pointed to by the index record, and proceed along the pointers in the file (that is, sequentially) until we find the desired record.

说白了,稠密索引和稀疏索引属于有序索引(ordered index)的一个分类

稠密索引:

  • 稠密索引的真实数据是按顺序储存的
  • 为每一个键都创建一个索引记录
  • 每个索引记录都包含键本身和指向实际数据的指针
  • 因为每个键都有索引,所以可以直接通过索引就找到目的键对应的数据

【MySQL笔记】正确的理解MySQL的索引机制以及内部实现(二)_第5张图片

稀疏索引:

  • 稀疏索引的真实数据是按顺序存储的
  • 只为部分的键创建索引记录
  • 当在稀疏索引中查找某个目的键时,通常会通过索引,先找到小于或等于目的键的其他键的数据项,既通过索引找到比目的键值要小的数据项(如果目的键有索引,就直接找到目的键的数据)。然后在数据项按顺序遍历(线性),直到找到目的键的数据记录。

【MySQL笔记】正确的理解MySQL的索引机制以及内部实现(二)_第6张图片
稀释索引怎么找目的数据?

  • 要找字段值为1003的数据,就要先找到小于或等于1003的键,比如1001
  • 通过1001的索引,我们找到1001所在的行数据,然后线性向下遍历两次,我们就找到了1003所在的行数据啦!
  • 这也是稠密和稀疏索引的前提,就是有序

稠密索引和稀疏索引的优缺点

  • 相对某列键而言,稠密索引对每个数据都建有索引,要查询起来,直接快速。但是因为要为每个数据都建立对应的索引,所以需要比较大的空间资源
  • 而稀疏索引因为只针对部分数据建立索引,所以空间资源占用小,但是查询效率相对比较慢

Clustered index is dense or sparse?


覆盖索引

覆盖索引(covering index),也称为索引覆盖。其实我更想称其为索引覆盖,以为我理解的覆盖更像是一个动词。因为覆盖索引其实并不是一种索引类型,而是一个技术性的提升查询效率的机制。

什么是覆盖索引?

  • 既在InnoDB中,只需要从辅助键索引中就可以查询到最终想要的数据结果,而不需要再从聚簇索引中二次查询。这么的一个技术手段,我们就称之为覆盖索引
  • 覆盖索引在MySQL中,仅仅是针对InnoDB存储引擎而言的。准确的说,是针对聚簇索引和非聚簇索引共存的情况下才能起作用的
  • 覆盖索引并不是一种索引类型,而是一种技术手段

举个应用覆盖索引的粟子!

在InnoDB存储引擎的表中:

  • 比如我们在某个表建立了一个普通组合索引(col1,col2,col3),由三个列组成。那么我们的select col1,col2,col3 from table where col1 = xxx;语句肯定会有覆盖索引的技术加持。只进行了一次(col1,col2,col3)辅助键索引,我们就可以得到(col1,col2,col3)三列数据的结果,自然也就不需要再拿到相关数据的主键,再跑到聚簇索引二次查询
  • 比如我们在某个表,以col1字段建议一个普通索引,那么我们的select col1 from table where col1 = xxx;, 也会得到覆盖索引的技术加持。

InnoDB和MyISAM下的B+树索引


InnoDB下的B+树索引

(一) 样例表

create table `student_innodb` (
	`sid` int(11) not 
     
      null
     ,
	`name` varchar(20) not 
     
      null
     ,
	`age` int(11) not 
     
      null
     ,
	`tel` varchar(11) not 
     
      null
     ,
	`email` varchar(20) not 
     
      null
     ,
	`class` varchar(20) not 
     
      null
     ,
	primary key (`sid`),
	unique name(`name`),
	
     
      index
      age(`age`),
	
     
      index
      tel_email(`tel`,`email`)
) engine=innodb default charset=utf8;

(二) InnoDB的表数据存放在哪?

我们知道,MySQL下的索引根据以列属性进行分类,可以分为主键索引辅助键索引。在InnoDB下,主键索引采用的是聚簇索引,辅助键索引采用的是非聚簇索引。

  • 主键索引的叶子结点的每个关键字对应的数据,存放的都是完整的行数据
  • 辅助键索引的叶子结点的每个关键字对应的数据,存放的都是主键信息

InnoDB的数据实际存储在哪?

  • 我们知道InnoDB的主键索引就是聚簇索引,聚簇索引的叶子结点存放的都是完整的数据。这就很容易的推断出,一张表的主键索引就已经一字不差的存放了整张表的所有数据。既该索引文件也是该表的直接数据存储文件
  • 既InnoDB下,一张表的所有数据都是存放在该表的聚簇索引叶子结点中的。

(三) InnoDB下利用索引查询的过程
下图为InnoDB下的走索引查询的过程
【MySQL笔记】正确的理解MySQL的索引机制以及内部实现(二)_第7张图片
主键查询

  • 通过主键查询数据,select * from student_innodb where sid = 1;
  • 直接走主键索引,从主键索引的根结点开始遍历,然后查到叶子结点,直接得到行数据

其他字段索引的查询

  • SQL条件不是主键,而是其他建立了索引的字段,假设是name。select * from student_innodb where name = 'jerry';
  • 首先查询以name字段建立的索引,该索引属于辅助键索引。所以从该name字段索引的根结点开始遍历,直到找到叶子结点,得到name = 'jerry'的行记录的主键信息sid = 1
  • 然后通过得到sid = 1的主键信息去主键索引中再次遍历,从根结点开始,直接叶子结点,便得到了行数据本身

所以,我们知道在InnoDB中,查询条件是主键的索引查询,只需要遍历一次索引树,而非主键的条件,这需要两次,先遍历辅助键索引得到对应的主键信息,再通过主键信息在主键索引中遍历,得到行数据


MyISAM下的B+树索引

(一) 样例表

create table `student_myisam` (
	`sid` int(11) not 
     
      null
     ,
	`name` varchar(20) not 
     
      null
     ,
	`age` int(11) not 
     
      null
     ,
	`tel` varchar(11) not 
     
      null
     ,
	`email` varchar(20) not 
     
      null
     ,
	`class` varchar(20) not 
     
      null
     ,
	primary key (`sid`),
	unique name(`name`),
	
     
      index
      age(`age`),
	
     
      index
      tel_email(`tel`,`email`)
) engine=myisam default charset=utf8;

(二) MyISAM的表数据存放在哪?

我们知道,MySQL下的索引根据以列属性进行分类,可以分为主键索引辅助键索引在MyISAM下,主键索引和辅助键索引都属于非聚簇索引。

  • 非聚簇索引的叶子结点的关键字的值都是对应数据在数据文件中的地址
  • 所谓地址,就是叶子结点存储的不是数据本身,而仅仅是一个指针

MyISAM的数据实际存储在哪?

  • 既MyISAM存储引擎的数据文本和索引是分开存储的,索引是索引,索引的叶子结点存储的也仅仅是指针
  • MyISAM下的表数据都是单独存储一个文件进行存储的,并不像InnoDB一样,存储在主键索引中,所以MyISAM不管查询是走主键索引,还是辅助键索引,通通都要遍历到叶子结点,从叶子结点获得真实数据的地址,再通过地址找到真实数据。

(三) MyISAM下利用索引查询的过程

下图为MyISAM下的走索引查询的过程
【MySQL笔记】正确的理解MySQL的索引机制以及内部实现(二)_第8张图片
通过主键查询:

  • 通过主键查询数据,select * from student_innodb where sid = 1;
  • 直接走主键索引,从主键索引的根结点开始遍历,然后查到叶子结点,得到行数据在数据文件中的地址
  • 通过地址,找到数据文件中的目的行数据

通过其他非主键字段查询:

  • SQL条件不是主键,而是其他建立了索引的字段,假设是name。select * from student_innodb where name = 'jerry';
  • 首先查询以name字段建立的索引,该索引属于辅助键索引。所以从该name字段索引的根结点开始遍历,直到找到叶子结点,得到目的行记录在数据文件的地址
  • 通过地址,找到数据文件中的目的行数据

所以,我们知道在MyISAM中,查询不管是走主键索引,还是非主键索引,在叶子结点得到的都是目的数据的地址,还需要通过该地址,才能在数据文件中找到目的数据(只需要一次遍历和一次的地址访问)


物理空间角度理解不同引擎下的数据存储

(一) InnoDB的数据文件
我们创建一个名为student_innodb的表,存储引擎使用InnoDB, 然后在搜索电脑中存放student_innodb表数据文件的地方
【MySQL笔记】正确的理解MySQL的索引机制以及内部实现(二)_第9张图片
可以看到两个文件,student_innodb.frmstudent_innodb.ibd。熟悉的人肯定知道,这两个文件就是student_innodb表的表定义文件和数据文件

  • student_innodb.frm
    .frm文件是一份定义文件,也就是定义student_innodb表是一张怎么样的表
  • student_innodb.ibd
    .ibd文件则是该表的索引,数据存储文件,既该表的所有索引树,所有行记录数据都存储在该文件中

总之,我们可以知道,InnoDB的表,数据存储文件只有一个,既.ibd文件

(二) MyISAM的数据文件
我们创建一个名为student_myisam的表,存储引擎使用MyISAM, 然后在搜索电脑中存放student_myisam表数据文件的地方
【MySQL笔记】正确的理解MySQL的索引机制以及内部实现(二)_第10张图片
我们可以看到三个文件, student_myisam.frm, student_myisam.MYDstudent_myisam.MYI文件。上面我们分析过了。.frm文件是表定义文件,不是数据文件。所以我们只关注MYD和MYI结尾的文件。

  • student_myisam.MYD
    .MYD文件是MyISAM存储引擎表的所有行数据的文件
  • student_myisam.MYI
    .MYI文件存放的是MyISAM存储引擎表的索引相关数据的文件

总之,我们可以简单的得出结论,MyISAM引擎下,表数据和表索引数据是分开存储的。

(三) 总结

  • MyISAM存储引擎中的表数据和表索引数据是分开文件进行存储的
  • InnoDB存储引擎中的表数据和表索引数据是在同一个文件存储的
  • 这也让我们更好的理解,为什么说InnoDB引擎下,整个表数据是存储在该表的聚簇索引树上的。

InnoDB、MyISAM下索引实现的区别和优缺点


InnoDB,MyISAM B+Tree索引模型的区别和优缺点对比,在广义上的角度,问题可以切换成聚簇索引和非聚簇索引的优缺点对比。因为InnoDB和MyISAM的B+Tree索引机制,很大程度上的区别,就是一个支持聚簇索引,一个不支持

(一) 聚簇索引的优缺点

聚簇索引的优点

  • 聚簇索引可以将相关的数据紧密的关联起来,存储在相邻的连续物理空间,利于范围查询,比如将相关的数据存放在一个叶子结点上,既一个结点的多个关键字对应的数据都存储在一个数据页中,范围查询时,磁盘一次Load出即可,降低IO操作次数,比如针对MAX, MIN, COUNT等聚集函数都有很好的作用。
  • 聚簇索引将数据和索引存储在同一个数据文件。 既聚簇索引的叶子结点不仅存放键的信息,还存储相关其他列的完全数据。当查询走聚簇索引,不需要中间人跳转,直接就可以获得目的数据,查询效率更快

聚簇索引的缺点

  • 因为聚簇索引是顺序存储的,如果多次的插入操作是以非顺序的方式执行,那么最终聚簇索引需要不断的维护这个顺序,这是需要一定性能消耗的。
  • 当聚簇索引中的主键发生更改时,可能需要重新维护顺序,迫使物理空间的交换,所以聚簇索引需要更长的时间来更新记录
  • 支持聚簇索引的存储引擎的辅助键索引的查询结果只是一个中间结果,还需要通过中间结果到聚簇索引上二次查询,操作相对繁琐

(二) 非聚簇索引的优缺点
非聚簇索引的优点

  • 因为一张表只能有一个聚簇索引,而非聚簇索引则可以有多个。所以非聚簇索引允许我们对一张表建立多个索引,提高数据库的查询性能
  • 非聚簇索引占用空间小,因为叶子结点不存储真实数据,所以非聚簇索引相比聚簇索引更小。
  • 在部分查询中,可以利用覆盖索引的特性,加快查询速度,直接从辅助键索引中获得想要的数据,而不需要做二次查询。
  • 非聚簇索引只需要一次遍历,便可得到数据地址。支持聚簇索引的存储引擎会导致其辅助键索引查询的结果只是一个中间结果,还需要通过该中间结果在聚簇索引再遍历一次。而不支持聚簇索引的存储引擎,只有非聚簇索引,非聚簇索引只需要一次遍历,即可得到真实数据的地址

非聚簇索引的缺点

  • 非聚集索引只能按逻辑顺序存储数据,并不允许以物理空间连续的方式对数据行进行顺序存储。既非聚簇索引一个叶子结点内部的所有关键字仅仅是逻辑顺序的维护。一个结点对应真实数据在数据文件中可能并非按连续物理空间存储的。 相对聚簇索引的查询,IO次数可能更多,查询性能更低
  • 相比聚簇索引,范围查询更慢,因为聚簇索引的范围查询可以让磁盘一次load出整个结点的数据线性遍历。虽然非聚簇索引的同叶子结点之间的关键字也是逻辑顺序存储,也可以线性遍历,但每线性遍历一个关键字都需要中间再跳转到另一个地方(InnoDB下的聚簇索引)遍历或(MyISAM下的数据文件)访问 。这个中间过程实际都是不同的IO操作,可能触发磁盘不同盘块的数据读取。所以本质还是会造成大量的IO操作。
  • 每当聚簇索引的主键值更新时,可能会触发非聚簇索引的更新,因为非聚簇索引的叶子结点可能存放的是主键信息(比如InnoDB)
  • 每当数据文件中的数据发生更新时, 也可能会触发非聚簇索引的更新,因为可能会导致非聚簇索引叶子结点的数据地址发生改变(比如MyISAM)

关于聚簇索引容易混淆的小贴士

我知道很多情况下,有的朋友在看了一些书籍或博客之后,会有一种认知倾向,认为InnoDB的索引采用的就是聚簇索引,而MyISAM的索引采用的是非聚簇索引的方式。虽然可以这么说,但其实这个概念也并非完全正确。

  • 因为索引是针对存储引擎才有意义的,而针对MySQL的存储引擎来说,准确的说,是InnoDB支持聚簇索引的概念,而聚簇索引的概念则体现在InnoDB的主键索引或其他特殊索引上。因为InnoDB支持聚簇索引,所以InnoDB可以将数据和索引存储在同一个文件。
  • 在细粒度的角度上考虑,是InnoDB不仅采用聚簇索引,也采用了非聚簇索引,因为在InnoDB中,主键索引属于聚簇索引,叶子结点不仅包含键值,还包含行中其他列的数据。而其辅助键索引则属于非聚簇索引,因为辅助键索引的叶子结点只包含键值以及主键数据。通过辅助键索引遍历之后得到主键信息,然后还要通过主键再在主键索引遍历一次,才能得到行数据。
  • MyISAM则是不管主键索引还是辅助键索引都属于非聚簇索引

所以总的来说,在广义上,通俗的角度来说,的确可以说InnoDB采用的是聚簇索引,MyISAM采用的是非聚簇索引。但更准确的说法是,InnoDB支持聚簇索引,MyISAM不支持聚簇索引

针对此文,最好可以看看官网对Clusered IndexSecondary Index的解释@Clustered and Secondary Indexes - @MySQL 官网


索引的其他底层实现


哈希索引

MySQL支持哈希索引,但是局限于部分的存储引擎,既MEMORY/HEAP和NDB。而常见的InnoDB和MyISAM则并不支持。

  • 虽然InnoDB不支持哈希索引,但是它依然有曲线救国的手段,就是支持一种伪哈希索引的方式,变相支持哈希索引。
  • 这样的伪哈希索引,我们叫它为自适应哈希索引。但这个自适应哈希索引并不是由我们人为控制建立的。而InnoDB存储引擎引擎自动优化创建,不受人为干预的
  • 但是我们可以通过参考来确认InnoDB是否开启自适应哈希索引模式

什么是自适应哈希索引?

  • 什么是哈希表,相信我们大家都知道,通过O(1)的时间复杂度,我们就可以查询到想要的数据,但是需要付出O(n)的空间复杂度代价。这也是InnoDB不支持哈希索引的原因之一
  • 那么什么是自适应哈希索引呢?说白了,它也是哈希索引,但是它不为表中的所有数据都建立索引。而是有选择性的为一些热点数据建立哈希索引。
  • 既Innodb存储引擎会监控对某表的辅助键索引查找情况,如果发现某辅助键索引被频繁访问,既代表某些关键字是热数据,于是这些数据则会被放入哈希索引中,由此让特定频繁被访问的热点数据可以享受到哈希索引O(1)的速度。

至此,我们可以知道InnoDB并不支持我们自建哈希索引,但是在某些情况下哈希索引的效率的确很高,于是InnoDB自己就为热点数据维护一套哈希索引,因为这套哈希索引并不是为所有数据建立的,而是由InnoDB动态监控,自行维护的,为部分热点数据优化速度而生。所以又名自适应哈希索引,以示区别。既自适应哈希索引是存储引擎自身的优化手段,并非提供出来给用户使用的索引类型。

引用至InnoDB关键特性之自适应hash索引

相比B+Tree索引,哈希索引有什么缺陷?

  • 哈希索引也没办法利用索引完成排序
  • 不支持最左匹配原则
  • 存在大量哈希冲突的情况下,哈希索引的效率也是极低的
  • 不支持范围查询

倒排索引

我们知道MySQL的InnoDB曾经是不支持MyISAM所独有的全文索引的。但是MySQL 5.6之后,InnoDB也支持全文索引FULLTEXT了。

  • 说是全文索引,其实它只代表可以实现全文检索的功能,而全文检索的底层实现,实际就是倒排索引,所以你也可以把全文索引的本质,当做是倒排索引。再了解全文检索之前,务必要先了解倒排索引

为什么叫倒排索引?

  • 因为英文单词Inverted有颠倒的意思,然后可能就被翻译为倒排索引,也有很多地方叫反向索引。相对而言的索引就是正排索引(forward index)
  • 倒排索引和正排索引,我的个人理解是,它与我们之前说的B+索引,哈希索引的应用不同。拿这些索引的知识去理解正排索引和倒排索引,感觉有些难以联系起来,容易混乱。因为正排,倒排是属于搜索引擎范畴的概念,所以理解正排,倒排最好还是先对搜索引擎有一些了解。比如使用过ElasticSearch

什么是倒排索引(Inverted index)?

说实话,倒排索引也不是一个简单的概念,很多搜索引擎的底层原理都是倒排索引,比如ElasticSearch等。
学习倒排索引的时候,最好还是需要一些搜索引擎的知识会更容易理解。

倒排索引就是相对正排索引相反而言的模型。正排索引是通过文档ID来遍历文档内容,找到关键字(文档 -> 关键字)。而倒排索引则是通过关键字,找到所在的文档(关键字 -> 文档)
【MySQL笔记】正确的理解MySQL的索引机制以及内部实现(二)_第11张图片
以上正排索引,假如我们的搜索引擎是基于正排索引实现的,那么我们要在索引库中查询"python"关键字, 就很可能需要一个一个文档的进行遍历,直到在某个文档的内容中找到"python"单词。这样的一个时间复杂度是很大的,尤其是数据量很大的情况下。所以基于正排索引实现的搜索引擎是不现实的
【MySQL笔记】正确的理解MySQL的索引机制以及内部实现(二)_第12张图片
如果在每一文档录入搜索引擎索引库的时候,我们就对文档的内容进行分词,统计,分析。建立倒排索引,记录分词得到的每个单词对应文档的出现位置和出现次数。 那么我们在搜索引擎查询"python"关键字,就会非常的快速,只需要通过关键字就可以马上查询到已经预处理的统计分析结果。也就知道了'python'关键字在文档A和文档B出现过。展示给用户即可。

这也是为什么是正排和倒排的原因,正排是通过文档查找关键字,倒排是通过关键字查到对应的文档。详细的倒排索引知识以后有时间再在ES的知识点中重点说明。
什么是倒排索引? - @作者:返回主页 大数据和人工智能躺过的坑
倒排索引为什么叫倒排索引?- @知乎


索引名词混乱的现状


对于索引名词混乱的个人感受

说实话呀,索引的概念就跟数据库的锁概念一样,四处充斥着各种各样的说法,各种各样的名词。对于初学者来说,十分的混淆。反正我整理本文时的心情是非常复杂的,所以我不能绝对的说,我表达的观点一定是正确的,但我努力让它是正确的。针对这样混乱的现象,我个人的感受是:

  • 数据库的设计和实现源于国外,文献是英文的。而针对同一个英文单词,可以由多个不同的中文单词去解释,比如聚集,聚簇的英文都可以是clustered。所以我揣测在文献资料翻译的过程中,因为作者的多元化,所以导致翻译结果也多元。最终导致针对同一名词,有着很多的中文单词描述
  • 国内的文献资料缺乏权威,网络资料质量参差不齐,错误的解释,一传十,十传百,形成习惯后,错误就成为了一种政治正确,最终导致国内含义和国外含义的不同
  • 概念复杂抽象,因为技术视野的局限性,很容易对概念造成错误的理解(比如我)。比如一些情况下,会有标准和实现的区分,标准只是一个模板,而实现才是真正的体现。也有一些情况下,他们是不同维度下概念,虽然有着相同或类似的名词,但实际表达着不同纬度的含义。
  • 还有一点的就是,数据库的具体实现众多,比如SQL Server,MySQL,PgSQL,Oracle等。 不同的关系型数据库之间,对于同一含义,可能存在不同的名词。

以上只是我的个人感受,如果因为是我的自身局限性导致将正确的结果误以为是一种混乱,希望大家告知!万分感谢


容易混淆的索引名词概念

为了避免概念的混淆,我将自己认为是正确的说法,描述一下,大致从三个方向进行分类
这里对数据库的各类索引解释的比较齐全

(一)数据索引底层实现原理

  • B树索引
    以B树作为底层数据结构而构成的索引树,就叫B树索引
  • B+树索引
    以B+树作为底层数据结构而构成的索引树,就叫B+树索引
  • BitMap索引
    以BitMap作为底层数据结构而构成的索引树,就叫BitMap索引
  • Hash索引
    以哈希表作为底层数据结构而构成的索引树,就叫Hash索引

(二)数据库支持的功能性索引

单列索引:

  • 主键索引
    以主键创建的索引,都叫主键索引
  • 唯一索引
    以唯一值创建的索引都叫唯一所以,主键索引也属于唯一索引的一种
  • 普通索引
    非主键,非唯一字段创建的索引都是普通索引,比如根据可重复的列创建的索引
  • 全文索引
    用于实现全文检索的索引模式

多列索引:

  • 组合索引
    将多个列的字段,组合起来,形成组合索引树

(三)数据库索引的内部实现的概念

主辅区分:

  • 主键索引(primary index)
    主键索引又称主索引
    以表的主键(primary key)创建的索引树,我们就称其为该表的主键索引
  • 辅助键索引(secondary index)
    辅助键索引,又称辅助索引次级索引二级索引
    辅助索引以表的非主键的字段创建的索引树,我们就称其为该表的辅助键索引

聚簇区分:

  • 聚簇索引(clustered index)
    聚簇索引又称聚集索引。针对MySQL而言,聚簇索引只存在于InnoDB中,再具体些,指代的就是InnoDB每个表的主键索引
  • 非聚簇索引(non-clustered index)
    非聚簇索引只是与聚簇索引想向的概念。针对MySQL而言,可以说InnoDB的辅助键索引,以及MyISAM的主、辅索引都是非聚簇索引

稠密区分:
准确的说,是 有序索引 (ordered Index)可以分为稠密索引和稀疏索引两类

  • 稠密索引(dense index)
    稠密索引,有的地方又称密集索引。稠密索引是指会为每一个数据都建立一个索引
  • 稀疏索引(sparse index)
    稀释索引是指只为部分的数据建立索引,通过索引先找到比目的数据要小的数据,然后顺序查找

(四)总结一下

  • 聚簇索引有些地方也称聚集索引
  • 辅助键索引(Secondary Index)通常称为辅助索引,有时也称为二级索引,次级索引
  • 主键索引(Primay Index)在InnoDB引擎下,有时候它与聚簇索引(Clusered Index)是等价的。
  • 主键索引(Primay Index)聚簇索引(Clusered Index)等价的情况下,例如InnoDB, 与主键索引(聚簇索引)相对的辅助键索引(辅助索引(Secondary Index))就是非聚簇索引(Non-Clusered Index)
  • 因为聚簇索引(Clusered Index)在InnoDB下又是一种有序索引(Ordered Index),所以聚簇索引又可以有具体的采用形式,可以是稠密索引形式(Dense Index),也可以是稀疏索引形式(Sparse Index)。

总而言之,中文博大精深,名词多样化,如果要得知一个名词最精确的含义,建议还是得知道它的英文名词。这一点在数据库索引具体实现内部分类的名词中最容易产生混淆,切记切记。然后根据不同的角度,又可以得到很多的分类,有些概念的边界也的确很难找,我也不想深究了,毕竟都快咬文嚼字了,这种感觉烦的很。


参考资料


  • 《MySQL技术内幕》

  • 深入理解MySQL索引原理和实现——为什么索引可以加速查询?

  • mysql在innodb索引下b+树的高度问题 - @作者: 面壁偷笑

  • InnoDB一棵B+树可以存放多少行数据?- @作者: 李平

  • Mysql聚簇索引和非聚簇索引原理(数据库) - @作者:小楼东风细雨

  • difference between sparse index and dense index - @stackoverflow

  • Indexing in Databases | Set 1 - @GeeksForGeeks

  • DBMS - Indexing - @tutorials-point

  • 聚集索引 非聚集索引 聚簇索引 稀疏索引 稠密索引 - @作者:douunderstand

  • Clustered vs Non-clustered Index: Key Differences with Example - @guru99 (推荐!!)

  • 如果觉得对你有帮助,能否点个赞或关个注,以示鼓励笔者呢?!

你可能感兴趣的:(MySQL)