:이 문서에서 재생되는 인덱스 어려워? 처음부터 끝까지 스트로크 인덱스 구조로 다시 MYSQL!
간단한 테이블의 시작부터
생성 나타난 사용자 ( 아이디 INT 기본 키 , 연령 INT , 높이 INT , 중량 INT , 이름 VARCHAR ( 32 ) ) 엔진 = 이노 단계;
학생들이 데이터베이스가이 문장을 이해할 수 있다고 생각 얻기, 우리는 또한 인덱스의 MySQL의 구조의 단계를 이해하여이 테이블 단계에서 시작하는 가장 쉬운 방법입니다.
첫째, 우리 테이블에 일부 데이터 삽입.
INSERT INTO의 사용자 (ID, 연령, 신장, 체중, 이름) VALUES ( 2 , 1 , 2 , 7 , ' 小吉' ); INSERT INTO의 사용자 (ID, 연령, 신장, 체중, 이름) VALUES ( 5 , 2 , 1 , 8 , ' 小尼' ); INSERT INTO의 사용자 (ID, 연령, 신장, 체중, 이름) VALUES ( 1 , 4 , 3 , 1 , '小泰' ); INSERT INTO의 사용자 (ID, 연령, 신장, 체중, 이름) VALUES ( 4 , 1 , 5 , 2 , ' 小美' ); INSERT INTO의 사용자 (ID, 연령, 신장, 체중, 이름) VALUES ( 3 , 5 , 6 , 7 , ' 小蔡' );
우리는 데이터가 테이블에 배치되어 있는지 확인합니다.
선택 * 에서 사용자 ;
당신은 데이터가 완전히 우리가 만든 사용자 테이블에 넣어왔다 볼 수 있습니다.
하지만 당신은 아직 아무것도 찾을 수 있는지 알고하지 않습니다, 우리는 데이터 순서가 보인다 삽입, 아주 이상한 일이 있었다 것 같다 ...
MySQL은 조용히 시퀀스 ID에 따라 줄을 우리에게 보인다.
왜 자동으로 우리가 그것을 주문 도움말을 줄 지어, 상황의 명시 적 분류하지 않고 우리의 MySQL에있다? 그것은 어떤 시간에 종류입니까?
소개 페이지
나는 과학 슬래그의 학습 직후 즉시 운영 체제로 졸업하는 시간을 알 수없는, 뇌의 페이지 개념은 아직 냉각되지 않습니다. 사실, MySQL은 또한 천천히 오는 유사한 페이지가, 내 말을 들어 논리적 저장 단위입니다.
우리는 디스크에 데이터의 크기를 취할 때 운영 체제의 개념에서, 데이터를 꺼낸 것으로 가정 1킬로바이트이지만, 운영 시스템은 데이터의 1킬로바이트을 제거하지 않지만, 4KB 데이터를 제거합니다 운영 체제 때문에 페이지 테이블 엔트리의 크기는 4킬로바이트입니다.
왜 우리는 데이터의 1킬로바이트이 필요하지만, 데이터의 운영 체제 4킬로바이트는 그것을 촬영하는?
이것은 로컬 프로그램의 개념, 내 다시 특정 개념이 불분명 포함, 아마도 "이며, 다시이 데이터 액세스이 데이터를 훨씬 더 가능성이있을 것입니다 이후에 액세스 할 수있는 데이터에 대한 방문 후 프로그램을 인접한 데이터 ", 그렇게 간단하게 메모리에 직접로드 및 데이터의 4킬로바이트, 당신은 메모리에서 직접 찾을 수있는 페이지의 데이터에 액세스 할 다음에, 당신이 디스크 IO를 줄일 수 있습니다
우리는 디스크 IO와 IO 메모리 속도가 매우 다르기 때문에 디스크 IO가 프로그램의 성능에 영향을 미치는 주요 요소임을 알고있다.
아마도 큰 부분, 또는 어떤 추상적 인, 그래서 우리는 단순히 데이터베이스 레벨에 페이지 개념의 새로운 이해를 반환하는 위의 설명을 읽고.
모든 것을 제쳐두고에도 불구하고, 가정은 우리가 삽입 된 데이터가, 우리가 지금 가장 원시적 인 방법에 따라, 우리가 생각하는 것, ID = 5 개 데이터를 찾고 있다는 것입니다 - 트래버스
예,이는 컴퓨터에게 데이터를 찾을 수있는 가장 일반적인 방법을 배우고 시작하는 우리의 시간입니다. 우리가 길을 통과하는 볼 그래서, 우리는 데이터 ID = 5를 찾아, 여러 디스크 IO를 통해 이동해야합니다.
첫째, 우리는 = 1 개 데이터가 처음부터 읽어 ID로 시작한 다음 결정해야하지, 다음, 무한히 데이터 ID = 2을하고 판단 할 경우 우리가 필요로하는 데이터 여부. 물론이 말을 한 후, 우리가 분류 MySQL의 도움으로, 우리는 다섯 디스크 IO를 통해 갈 필요가 , 5 번 데이터를 발견하고 판독 할 수있다.
그래서 우리가 데이터를 읽는 방법 페이지의 개념을 도입 한 후 살펴 보자.
메모리에 페이지의 개념을 도입 한 후, MySQL은 "페이지"데이터 구조라고 복수의 데이터를 존재하게 데이터 MySQL의 ID = 1을 읽고, 전체 페이지 의지 페이지 ID는 = 1 개 곳의 데이터 다음 메모리 판정 횡단
메모리가 디스크 IO 너무 상대, 디스크 IO 속도보다 훨씬 높은이므로, 거의 무시할 수, 우리는 우리가 (각 페이지가 4 개 데이터를 저장할 수 있다는 가정) 디스크 IO를 읽고 여러 번 통과해야하는이 데이터를보십시오.
처음 우리는 모든 메모리 (4)에 데이터 ID가 1이 첫번째 디스크 IO, 데이터 ID = 5의 제 읽는 = 데이터 ID = 1 및 = ID를 읽어 메모리에, 상기 제 디스크 IO는이다.
우리가 보조 디스크 IO를 통해 갈 필요가 그래서 당신은이 데이터 ID = 5를 찾을 수 있습니다.
그러나 사실, MySQL의 이노 디비 엔진에서 페이지 크기가 16킬로바이트 4 회 운영 체제이며, 데이터 INT의 유형은 4 바이트 데이터이다 일반적 미만 4000 바이트의 다른 유형의 바이트 수, 반면 그래서 하나는, 많은 양의 데이터를 저장할 수있는 MySQL의 데이터가 페이지 조합의 기본 단위입니다 .
우리는지도에 지금까지 이해 페이지 구조는 우리가 추가로 MySQL의 데이터 페이지, 다음 다음 페이지와 이전 페이지에 대한 포인터에 대한 포인터를 가지고있는 복수의 데이터가 포함되어 있습니다.
여기에서 다음 사실, 아래로 한 후, 우리는 데이터를 삽입, 여기서 첫 번째 비밀을 정렬 원하는 이유에 우리가, 주문 페이지에서 줄 수 있도록 할 때 MySQL은 실제로 첫 번째 질문에 대답 할 수있다 찾습니다.
성능에 영향을 정렬
우리는 질문을 물었다보다도 당신이 그것을 정렬 할 데이터를 삽입 할 때 왜 데이터베이스를? 정상적인 순서로 데이터를 삽입 우리는 또한 당신을 위해 매우 좋지 않다?
이것은 우리가 절대적으로 그렇게 데이터를 삽입하면 일종의 목적을 가지고해야 할 때, 당신은 하나의 작업에서 데이터가 프로세스가 복잡하기 위해 삽입 아무 이유없이 증가하지 않을 어떤 경우에 데이터베이스 쿼리 프로세스에 관련된 문제 일 것이다 쿼리의 효율성을 최적화 .
而我们不难看出,页内部存放数据的模块,实质上就是一个链表的结构,链表的特点也就是增删快,查询慢,所以优化查询的效率是必须的。
基于单页模式存储的查询流程
还是基于我们第一节中的那张页图来谈,我们插入了五条数据,id分别是从1-5,那么假设我要找一个表中不存在的id,假设id=-1,那么现在的查询流程就是:
将id=1的这一整页数据取出,进行逐个比对,那么当我们找到id=1的这条数据时,发现这个id大于我们所需要找的哪个id,由于数据库在插入数据时,已经进行过排序了,那么在id=1的数据后面,都是id>1的数据,所以我们就不需要再继续往下寻找了。
如果在插入时没有进行排序,那毋庸置疑,我们需要再继续往下进行寻找,逐条查找直到到结尾也没有找到这条数据,才能返回不存在这条数据。
当然,这只是排序优化的冰山一角,接着往下看。
上述页模式可能带来的问题
说完了排序,下面就来分析一下我们在第一节中的那幅图,对于大数据量下有什么弊端,或者换一个说法,我们可以怎么对这个模式进行优化。
我们不难看出,在现阶段我们了解的页模式中,只有一个功能,就是在查询某条数据的时候直接将一整页的数据加载到内存中,以减少硬盘IO次数,从而提高性能。
但是,我们也可以看到,现在的页模式内部,实际上是采用了链表的结构,前一条数据指向后一条数据,本质上还是通过数据的逐条比较来取出特定的数据。
那么假设,我们这一页中有一百万条数据,我们要查的数据正好在最后一个,那么我们是不是一定要从前往后找到这一条数据呢?
如果是这样,我们需要查找的次数就达到了一百万次,即使是在内存中查找,这个效率也是不高的。那么有什么办法来优化这种情况下的查找效率呢?
页目录的引入
我们可以打个比方,我们在看书的时候,如果要找到某一节,而这一节我们并不知道在哪一页,我们是不是就要从前往后,一节一节地去寻找我们需要的内容的页码呢?
答案是否定的,因为在书的前面,存在目录,它会告诉你这一节在哪一页,例如,第一节在第1页、第二节在第13页。在数据库的页中,实际上也使用了这种目录的结构,这就是页目录。
那么引入页目录之后,我们所理解的页结构,就变成了这样:
分析一下这张图,实际上页目录就像是我们在看书的时候书本的目录一样,目录项1就相当于第一节,目录项2就相当于第二节,而每一条数据就相当于书本的每一页
这张图就可以解释成,第一节从第一页开始,第二节从第三页开始,而实际上,每个目录项会存放自己这个目录项当中最小的id,也就是说,目录项1中会存放1,而目录项2会存放3。
那么对比一下数据库在没有页目录时候的查找流程,假设要查找id=3的数据,在没有页目录的情况下,需要查找id=1、id=2、id=3,三次才能找到该数据,而如果有页目录之后,只需要先查看一下id=3存在于哪个目录项下,然后直接通过目录项进行数据的查找即可,如果在该目录项下没有找到这条数据,那么就可以直接确定这条数据不存在,这样就大大提升了数据库的查找效率
但是这种页目录的实现,首先就需要基于数据是在已经进行过排序的的场景下,才可以发挥其作用,所以看到这里,大家应该明白第二个问题了,为什么数据库在插入时会进行排序,这才是真正发挥排序的作用的地方。
页的扩展
在上文中,我们基本上说明白了MySQL数据库中页的概念,以及它是如何基于页来减少磁盘IO次数的,以及排序是如何优化查询的效率的。
那么我们现在再来思考第三个问题:在开头说页的概念的时候,我们有说过,MySQL中每一页的大小只有16KB,不会随着数据的插入而自动扩容,所以这16KB不可能存下我们所有的数据,那么必定会有多个页来存储数据,那么在多页的情况下,MySQL中又是怎么组织这些页的呢?
针对这个问题,我们继续来画出我们现在所了解的多页的结构图:
可以看到,在数据不断变多的情况下,MySQL会再去开辟新的页来存放新的数据,而每个页都有指向下一页的指针和指向上一页的指针,将所有页组织起来(这里修改了一下数据,将每一列的数据都放到了数据区中,其中第一个空格之前的代表id),第一页中存放id为1-5的数据,第二页存放id为6-10的数据,第三页存放id为11-15的数据,需要注意的是在开辟新页的时候,我们插入的数据不一定是放在新开辟的页上,而是要进行所有页的数据比较,来决定这条插入的数据放在哪一页上,而完成数据插入之后,最终的多页结构就会像上图中画的那样。
多页模式
在多页模式下,MySQL终于可以完成多数据的存储了,就是采用开辟新页的方式,将多条数据放在不同的页中,然后同样采用链表的数据结构,将每一页连接起来。那么可以思考第四个问题:多页情况下是否对查询效率有影响呢?
多页模式对于查询效率的影响
针对这个问题,既然问出来了,那么答案是肯定的,多页会对查询效率产生一定的影响,影响主要就体现在,多页其本质也是一个链表结构,只要是链表结构,查询效率一定不会高。
假设数据又非常多条,数据库就会开辟非常多的新页,而这些新页就会像链表一样连接在一起,当我们要在这么多页中查询某条数据时,它还是会从头节点遍历到存在我们要查找的那条数据所存在的页上,我们好不容易通过页目录优化了页中数据的查询效率,现在又出现了以页为单位的链表,这不是前功尽弃了吗?
如何优化多页模式
由于多页模式会影响查询的效率,那么肯定需要有一种方式来优化多页模式下的查询。相信有同学已经猜出来了,既然我们可以用页目录来优化页内的数据区,那么我们也可以采取类似的方式来优化这种多页的情况。
是的,页内数据区和多页模式本质上都是链表,那么的确可以采用相同的方式来对其进行优化,它就是目录页。
所以我们对比页内数据区,来分析如何优化多页结构。在单页时,我们采用了页目录的目录项来指向一行数据,这条数据就是存在于这个目录项中的最小数据,那么就可以通过页目录来查找所需数据。
所以对于多页结构也可以采用这种方式,使用一个目录项来指向某一页,而这个目录项存放的就是这一页中存放的最小数据的索引值。和页目录不同的地方在于,这种目录管理的级别是页,而页目录管理的级别是行。
那么分析到这里,我们多页模式的结构就会是下图所示的这样:
存在一个目录页来管理页目录,目录页中的数据存放的就是指向的那一页中最小的数据。
这里要注意的一点是:其实目录页的本质也是页,普通页中存的数据是项目数据,而目录页中存的数据是普通页的地址。
假设我们要查找id=19的数据,那么按照以前的查找方式,我们需要从第一页开始查找,发现不存在那么再到第二页查找,一直找到第四页才能找到id=19的数据
但是如果有了目录页,就可以使用id=19与目录页中存放的数据进行比较,发现19大于任何一条数据,于是进入id=16指向的页进行查找,直接然后再通过页内的页目录行级别的数据的查找,很快就可以找到id为19的数据了。随着数据越来越多,这种结构的效率相对于普通的多页模式,优势也就越来越明显。
回归正题,相信有对MySQL比较了解的同学已经发现了,我们画的最终的这幅图,就是MySQL中的一种索引结构——B+树。
B+树的引入
B+树的特点我在《[从入门到入土]令人脱发的数据库底层设计》已经有详细叙述过了,在这里就不重复叙述了,如果有不了解的同学可以去看这篇博客。
我们接着往下聊,我们将我们画的存在目录页的多页模式图宏观化,可以形成下面的这张图:
这就是我们兜兜转转由简到繁形成的一颗B+树。和常规B+树有些许不同,这是一棵MySQL意义上的B+树,MySQL的一种索引结构,其中的每个节点就可以理解为是一个页,而叶子节点也就是数据页,除了叶子节点以外的节点就是目录页。
这一点在图中也可以看出来,非叶子节点只存放了索引,而只有叶子节点中存放了真实的数据,这也是符合B+树的特点的。
B+树的优势
由于叶子节点上存放了所有的数据,并且有指针相连,每个叶子节点在逻辑上是相连的,所以对于范围查找比较友好。
B+树的所有数据都在叶子节点上,所以B+树的查询效率稳定,一般都是查询3次。
B+树有利于数据库的扫描。
B+树有利于磁盘的IO,因为他的层高基本不会因为数据扩大而增高(三层树结构大概可以存放两千万数据量。
页的完整结构
说完了页的概念和页是如何一步一步地组合称为B+树的结构之后,相信大家对于页都有了一个比较清楚的认知,所以这里就要开始说说官方概念了,基于我们上文所说的,给出一个完整的页结构,也算是对上文中自己理解页结构的一种补充。
上图为 Page 数据结构,File Header 字段用于记录 Page 的头信息,其中比较重要的是 FIL_PAGE_PREV 和 FIL_PAGE_NEXT 字段,通过这两个字段,我们可以找到该页的上一页和下一页,实际上所有页通过两个字段可以形成一条双向链表。
Page Header 字段用于记录 Page 的状态信息。接下来的 Infimum 和 Supremum 是两个伪行记录,Infimum(下确界)记录比该页中任何主键值都要小的值,Supremum (上确界)记录比该页中任何主键值都要大的值,这个伪记录分别构成了页中记录的边界。
User Records 中存放的是实际的数据行记录,具体的行记录结构将在本文的第二节中详细介绍。Free Space 中存放的是空闲空间,被删除的行记录会被记录成空闲空间。Page Directory 记录着与二叉查找相关的信息。File Trailer 存储用于检测数据完整性的校验和等数据。
基于B+树聊聊MySQL的其它知识点
看到这里,我们已经了解了MySQL从单条数据开始,到通过页来减少磁盘IO次数,并且在页中实现了页目录来优化页中的查询效率,然后使用多页模式来存储大量的数据,最终使用目录页来实现多页模式的查询效率并形成我们口中的索引结构——B+树。既然说到这里了,那我们就来聊聊MySQL的其他知识点。
聚簇索引和非聚簇索引
关于聚簇索引和非聚簇索引在[从入门到入土]令人脱发的数据库底层设计这篇文章中已经有了详细的介绍,这里简单地说说
所谓聚簇索引,就是将索引和数据放到一起,找到索引也就找到了数据,我们刚才看到的B+树索引就是一种聚簇索引,而非聚簇索引就是将数据和索引分开,查找时需要先查找到索引,然后通过索引回表找到相应的数据。InnoDB有且只有一个聚簇索引,而MyISAM中都是非聚簇索引。
联合索引的最左前缀匹配原则
在MySQL数据库中不仅可以对某一列建立索引,还可以对多列建立一个联合索引,而联合索引存在一个最左前缀匹配原则的概念,如果基于B+树来理解这个最左前缀匹配原则,相对来说就会容易很很多了。
首先我们基于文首的这张表建立一个联合索引:
create index idx_obj on user(age asc,height asc,weight asc)
我们已经了解了索引的数据结构是一颗B+树,也了解了B+树优化查询效率的其中一个因素就是对数据进行了排序,那么我们在创建idx_obj这个索引的时候,也就相当于创建了一颗B+树索引,而这个索引就是依据联合索引的成员来进行排序,这里是age,height,weight。
看过我之前那篇博客的同学知道,InnoDB中只要有主键被定义,那么主键列被作为一个聚簇索引,而其它索引都将被作为非聚簇索引,所以自然而然的,这个索引就会是一个非聚簇索引。
所以根据这些我们可以得出结论:
-
idx_obj这个索引会根据age,height,weight进行排序
-
idx_obj这个索引是一个非聚簇索引,查询时需要回表
根据这两个结论,首先需要了解的就是,如何排序?
单列排序很简单,比大小嘛,谁都会,但是多列排序是基于什么原则的呢(重点)?
实际上在MySQL中,联合索引的排序有这么一个原则,从左往右依次比较大小,就拿刚才建立的索引举例子,他会先去比较age的大小,如果age的大小相同,那么比较height的大小,如果height也无法比较大小, 那么就比较weight的大小,最终对这个索引进行排序。
那么根据这个排序我们也可以画出一个B+树,这里就不像上文画的那么详细了,简化一下:
数据:
B+树:
注意:此时由于是非聚簇索引,所以叶子节点不在有数据,而是存了一个主键索引,最终会通过主键索引来回表查询数据。
B+树的结构有了,就可以通过这个来理解最左前缀匹配原则了。
我们先写一个查询语句
SELECT * FROM user WHERE age=1 and height = 2 and weight = 7
毋庸置疑,这条语句一定会走idx_obj这个索引。
那么我们再看一个语句:
SELECT * FROM user WHERE height=2 and weight = 7
思考一下,这条SQL会走索引吗?
答案是否定的,那么我们分析的方向就是,为什么这条语句不会走索引。
上文中我们提到了一个多列的排序原则,是从左到右进行比较然后排序的,而我们的idx_obj这个索引从左到右依次是age,height,weight,所以当我们使用height和weight来作为查询条件时,由于age的缺失,那么就无法从age来进行比较了。
看到这里可能有小伙伴会有疑问,那如果直接用height和weight来进行比较不可以吗?
显然是不可以的,可以举个例子
我们把缺失的这一列写作一个问号,那么这条语句的查询条件就变成了?27,那么我们从这课B+树的根节点开始,根节点上有127和365,那么以height和weight来进行比较的话,走的一定是127这一边,但是如果缺失的列数字是大于3的呢?比如427,527,627,那么如果走索引来查询数据,将会丢失数据,错误查询。所以这种情况下是绝对不会走索引进行查询的。这就是最左前缀匹配原则的成因。
-
最左前缀匹配原则,MySQL会一直向右匹配直到遇到范围查询(>、<、between、like)就停止匹配,比如 a="3" and="" b="4" c="">5 and d=6,如果建立(a,b,c,d)顺序的索引,d是无法使用索引的,如果建立(a,b,d,c)的索引则都可以使用到,a、b、d的顺序可以任意调整。
-
=和in可以乱序,比如 a=1 and b=2 and c=3 建立(a,b,c)索引可以任意顺序,MySQL的查询优化器会帮你优化成索引可以识别的形式。
根据我们了解的可以得出结论:
只要无法进行排序比较大小的,就无法走联合索引。
可以再看几个语句:
SELECT * FROM user WHERE age=1 and height = 2
这条语句是可以走idx_obj索引的,因为它可以通过比较 (12?<365)。
SELECT * FROM user WHERE age=1 and weight=7
这条语句也是可以走ind_obj索引的,因为它也可以通过比较(1?7<365),走左子树,但是实际上weight并没有用到索引,因为根据最左匹配原则,如果有两页的age都等于1,那么会去比较height,但是height在这里并不作为查询条件,所以MySQL会将这两页全都加载到内存中进行最后的weight字段的比较,进行扫描查询。
SELECT * FROM user where age>1
这条语句不会走索引,但是可以走索引。这句话是什么意思呢?
这条SQL很特殊,由于其存在可以比较的索引,所以它走索引也可以查询出结果,但是由于这种情况是范围查询并且是全字段查询,如果走索引,还需要进行回表,MySQL查询优化器就会认为走索引的效率比全表扫描还要低,所以MySQL会去优化它,让他直接进行全表扫描。
SELECT * FROM user WEHRE age=1 and height>2 and weight=7
这条语句是可以走索引的,因为它可以通过age进行比较,但是weight不会用到索引,因为height是范围查找,与第二条语句类似,如果有两页的height都大于2,那么MySQL会将两页的数据都加载进内存,然后再来通过weight匹配正确的数据。
为什么InnoDB只有一个聚簇索引,而不将所有索引都使用聚簇索引?
因为聚簇索引是将索引和数据都存放在叶子节点中,如果所有的索引都用聚簇索引,则每一个索引都将保存一份数据,会造成数据的冗余,在数据量很大的情况下,这种数据冗余是很消耗资源的。
补充两个关于索引的点
这两个点也是上次写关于索引的博客时漏下的,这里补上。
1.什么情况下会发生明明创建了索引,但是执行的时候并没有通过索引呢?
科普时间:查询优化器 一条SQL语句的查询,可以有不同的执行方案,至于最终选择哪种方案,需要通过优化器进行选择,选择执行成本最低的方案。
在一条单表查询语句真正执行之前,MySQL的查询优化器会找出执行该语句所有可能使用的方案,对比之后找出成本最低的方案。这个成本最低的方案就是所谓的执行计划。
优化过程大致如下:
1、根据搜索条件,找出所有可能使用的索引
2、计算全表扫描的代价
3、计算使用不同索引执行查询的代价
4、对比各种执行方案的代价,找出成本最低的那一个 。
根据我们刚才的那张表的非聚簇索引,这条语句就是由于查询优化器的作用,造成没有走索引:
SELECT * FROM user where age>1
2.在稀疏索引情况下通常需要通过叶子节点的指针回表查询数据,什么情况下不需要回表?
科普时间:覆盖索引 覆盖索引(covering index)指一个查询语句的执行只用从索引中就能够取得,不必从数据表中读取。也可以称之为实现了索引覆盖。
当一条查询语句符合覆盖索引条件时,MySQL只需要通过索引就可以返回查询所需要的数据,这样避免了查到索引后再返回表操作,减少I/O提高效率。
如,表covering_index_sample
中有一个普通索引 idx_key1_key2(key1,key2)
。当我们通过SQL语句:select key2 from covering_index_sample where key1 = 'keytest';
的时候,就可以通过覆盖索引查询,无需回表。
例如:
SELECT age FROM user where age = 1
这句话就不需要进行回表查询。
结语
本篇文章着重聊了一下关于MySQL的索引结构,从零开始慢慢构建了一个B+树索引,并且根据这个过程谈了B+树是如何一步一步去优化查询效率的。
简单地归纳一下就是:
排序:优化查询的根本,插入时进行排序实际上就是为了优化查询的效率。
页:用于减少IO次数,还可以利用程序局部性原理,来稍微提高查询效率。
页目录:用于规避链表的软肋,避免在查询时进行链表的扫描。
多页:数据量增加的情况下开辟新页来保存数据。
目录页:“特殊的页目录”,其中保存的数据是页的地址。查询时可以通过目录页快速定位到页,避免多页的扫描。