索引是存储引擎用于快速找到数据记录的一种数据结构,就好比一本教科书的目录,通过目录可以找到对应文章的页面,而不用一页一页从头翻到尾.MySQL也是一样的道理.在进行数据查找时,首先查看查询条件是否命中某条索引,符合则通过索引查找相关数据,如果不符合,则需要全表扫描,一条一条的查找记录,直到查找到目标数据.
为了减少磁盘的I/O次数,提高查询效率,我们就需要用到索引.
1). 索引的定义
索引是帮助MySQL高效获取数据的数据结构.
2). 本质
索引是数据结构. 可以简单理解为"排好序的快速查找数据结构". 满足特定的查找算法. 这些数据以某种方式指向数据,这样就可以在这些数据结构的基础上实现高效的查找算法.
索引是在存储引擎中实现的,因此每种存储引擎的索引不一定完全相同.(后面我们知道,像InnoDb, MyISAM等存储引擎的索引底层是b+树,而Memory的索引底层是哈希表). 并且每种存储索引不一定支持所有索引类型. 同时,存储引擎可以定义每个表的最大索引数和最大索引长度. 所有索引引擎支持每个表至少16个索引,总索引长度至少256字节.
增加索引也有很多不利的地方.
索引可以提高查询速度,但会影响插入记录的速度.这种情况下,我们可以先删除表中的索引,然后插入数据完成后,再创建索引.
比如 : select 列名 from 表名 where 列名=xxx
1). 在一个页中的查找.
假设目前表中的数据非常少,所有数据都可以被放到一个页中(一个页的存储大小为16kb),在查找记录的时候可以根据搜索条件的不同分为两种情况.
2). 在多个页中查找.
大多数情况下我们表中存放的记录是非常多的,需要好多页来存放我们的数据.在多个页中查找记录可以分为两个步骤.
1. 定位到目标记录所在的页.
2. 从所在的页中查找相应的记录.
在没有索引的情况下,不论是根据主键列或者其他列的值进行查找,由于我们不能快速定位到目标记录所在的页,所以只能从第一个页开始沿着双向链表一直找下去,在每个页中根据上述查找方法去查找目标记录.因为要遍历所有的数据页,这种方式是非常耗时的.
主键列聚簇索引.
- CREATE TABLE index_demo(
- c1 INT,
- c2 INT,
- c3 varchar(10),
- PRIMARY KEY(c1)
- ) ROW_FORMAT=COMPACT;
依据主键列c1创建了聚簇索引.记录行格式为COMPACT.规定了一条记录的组成部分为变长字段长度列表,null值列表,记录头信息,以及真实的数据.其中记录头信息包含record_type和next_record字段.下图简化了行格式,略去不必要的信息.

把一些记录放在页里的示意图就是 :

依据主键值的大小串成一个单向链表.
此时我们insert插入数据.假设一个页只能存放三条记录,只是假设,实际上可能存放很多条记录.
- INSERT INTO index_demo
- VALUES (1, 10, 'yy'),
- (3, 100, 'xx'),
- (2, 1000, 'zz');
即使我们添加的记录主键值并不是递增的,但也会调整为主键值递增的.如果我们此时再增加一条主键值为2的记录.由于要求下一条数据页中的记录大于上一条数据页中的记录,所以在插入主键值为2的记录时需要伴随一次记录移动,也就是把主键值为3的记录移动到主页11,把主键为2的记录移动到页10.这个过程称为页的分裂.

给所有页建立目录项.
这些16kb的页在物理存储上是双向链表,是不连续的,所以如果想从这么多页中根据主键值快速定位某些记录所在的页,我们需要给它们做个目录,每个页对应一个目录项,每个目录项包含两个部分.

可以为目录项建立目录页.目录项之间以单向链表存储.

从图中可以看出,我们新分配了一个页号为30的页专门存储目录项记录.
目录项记录与普通用户记录的不同点.
目录项记录与普通用户记录的相同点.
虽然说目录项记录只存储主键值和对应的页号,但不论怎么说,一个页只有16kb大小,存放的目录项记录也是有限的,如果表数据太大,以至于一个数据页不足以存放所有的目录项记录.所以有必要再产生目录页.
这些目录页是不连续的,如果我们表中数据记录非常多则会产生很多的存储目录项记录的页,那么我们怎么根据主键值快速定位一个存储目录项记录的页呢?所以有必要为这些存储目录项记录的页再生成一个更高级的目录.就像是一个多级目录,大目录里嵌套小目录.小目录里才是实际的数据.

我们可以用图来描述索引.

叶子节点存放真实的数据,非叶子节点存放目录项.
页与页之间以双向链表存储,记录与记录(不论是数据项记录还是目录项记录)之间是以单向链表存储.