在上一节,咱们聊到数据库为了让咱们的查询加速,经过索引方式对数据进行冗余并排序,这样咱们在使用时就能够在排好序的数据里进行快速的二分查找,使得查询效率指数提高。可是我在结尾一样提到一个问题,就是内存大小通常是颇有限的,不可能把一个表全部的数据都加载到内存中,那么咱们该如何解决这个问题呢?在解决这个问题以前,须要先简单了解一下硬盘知识html
因为机械硬盘的高耐久,低成本,如今仍然是数据存储的主流,因此这里着重讨论机械硬盘,下面是一个机械硬盘结构图linux
机械硬盘的数据都存放在盘片中,当咱们从硬盘读取数据时,咱们须要提供一个地址,而后硬盘经过先后移动磁头寻址,最后把地址对应数据返回。sql
这里有两个过程很重要,一个是寻址,一个是读取数据。以目前机械硬盘的速度,若是咱们要从机械硬盘读取一条1KB的数据大概只须要0.01ms(100MB/s),而寻址却平均在10ms左右。一般咱们把读取一段连续的数据,不须要屡次寻址的操做叫作顺序读,而读取不连续的数据须要屡次寻址的操做叫作随机读,用来区分它们之间的性能差距。数据库
为了充分利用机械硬盘的性能,一般把相关数据连续保存,这样就能够一次加载更多的数据,减小磁头的的移动次数。操做系统有不少对此的优化,例如Linux ext3文件系统默认块大小就是4kb。还有linux预加载能力,即当你频繁访问一块数据时,系统会帮你把相邻的数据也加载进来。性能
了解完机械硬盘的基本知识,如今回到MySQL,MySQL InnoDB引擎也会把数据进行分块存储,默认是16KB。因此咱们上一节中的索引结构图在硬盘中的存储就是每16KB为一个块,当一个块快存放快满的时候开辟一个新的块来存放。学习
以books表为例优化
create table books( id int not null primary key auto_increment, name varchar(255) not null, author varchar(255) not null, created_at datetime not null default current_timestamp, updated_at datetime not null default current_timestamp on update current_timestamp, index idx_books_name(name) )engine=InnoDB;
该表name字段的索引idx_books_name在硬盘中的存放就以下图spa
当块愈来愈多的时候,咱们可能没法一次把全部的块都加载到内存,此时就要对每一个块再进行索引,以下图:操作系统
每一个块的上一级都存放着一条指向该块首记录的记录。这样只须要加载顶部的第一块,而后经过区间判断就能够找到下一块的地址。3d
例如咱们查询一条name=name n+1的记录,过程以下:
1. 先从左边顶部块a开始查找,发现"name n+1"在"name 1"到"name m"记录之间
2. 加载"name 1"对应的下一级块b
3. 发现"name n+1"在块b第二条记录到第三条记录之间,因此须要加载第二条记录对应的下一级块d
4. 加载块d
5. 在块d中找到"name n+1"的那条记录。
若是把上图旋转一个,能够发现,整个图就是一个树,这其实就是B+树。B+树经过对数据块进行索引,使得当数据量很大,没法一次所有加载到内存时,能够先加载一个表的顶部数据块,而后根据数据所在区间再加载下一级的数据块。这样既保证了咱们的快速搜索,又减小了内存使用。
了解了B+树,如今就能够很容易区分MySQL的聚簇索引和二级索引。
聚簇索引就是用主键生成B+树,在叶子节点存放这条记录的完整信息
二级索引就是用索引行生成B+树,在叶子节点只存放索引行和该行对应的主键信息
下面是聚簇索引和二级索引的区分图
了解上面的知识,对于一个查询,咱们就能够大概想象出他的执行步骤
select * from books where name = "name400";
例如上面sql的执行步骤以下:
1. 在二级索引idx_books_name索引中查找name="name400"的字段所对应的主键id
2. 经过主键id在聚簇索引找到此id所对应的记录
3. 返回记录中的全部字段
当咱们select的字段在二级索引上不存在时,都须要使用聚簇索引回表查询剩余字段。因此聚簇索引,也就是咱们所说的id列,占用空间越小越好, 这样就能够在一个节点中存放更多的id值,减小树的层级,加速查询效率。通常推荐主键使用int或者bigint而不是字符串。同时最好保证插入的id值为递增的,这样就不会形成在一个已经满的节点中插入一条记录形成页分裂,下降查询效率。
这节咱们先了解了硬盘的基础知识,知道了机械硬盘的顺序读与随机读的巨大性能差距,以及操做系统为了优化磁盘性能而把数据进行按块存储。而后又学习了MySQL经过使用B+树,把存放索引的多个数据块进行索引,解决了咱们上一节使用二分搜索须要先把全部数据都加载到内存的问题。最后,咱们了解了聚簇索引和二级索引的区别,以及其中的使用建议。
下一节,咱们会聊一聊如何建立一个好的索引,判断一个索引的好坏标准有哪些。