简介
索引是应用程序设计和开发的一个重要方面。如果索引太多,应用的性能可能会受到影响;如果索引太少,对查询性能又会产生影响。
一、InnoDB存储引擎索引概述
一些开发人员总是在事后才想起添加索引,这是一种错误的开发模式。
InnoDB存储引擎支持两种常见的索引,一种是B+树索引,另一种是哈希索引。InnoDB存储引擎支持的哈希索引是自适应的,InnoDB存储引擎会根据表的使用情况自动为表生产哈希索引,不能人为干预是否在一张表中生成哈希索引。
B+树索引就是传统意义上的索引,这是目前关系型数据库中最常用、最有效的索引。B+树索引的构造类似二叉树,根据键值快速找到数据。B+树的B不代表二叉(binary),而是代表平衡(balance),因为B+树最早是从平衡二叉树演化而来,但是B+树不是一个二叉树。
B+树索引并不能找到一个给定键值得具体行。B+树索引能找到的只是被查找数据行所在的页。然后数据库通过把页读入内存,再在内存中进行查找,最后得到查找的数据。
二、二分查找法
每页Page Directory中的槽是按照主键的顺序存放的,对于某一条具体记录的查询是通过对Page Directory进行二分查找得到的。
三、平衡二叉树
B+树是通过二叉查找树,再由二叉平衡树、B树演化而来。
二叉查找树中,左子树的键值总是小于根的键值,右子树的键值总是大于根的键值。因此可以通过中序遍历得到键值得排序输出。
由于存在极端单边树的情况,所以要想最大性能的构造一个二叉查找树,需要这棵二叉查找树是平衡的,因此引入了新的定义—平衡二叉树,或称为AVL树。
平衡二叉树定义:首先符合二叉查找树的定义,其次必须满足任何节点的左右两个子树的高度最大差为1。
平衡二叉树对于查询速度的确很快,但是维护一颗平衡二叉树的代价是非常大的,通常需要1次或多次左旋和右旋来得到插入或更新后树的平衡性。
四、B+树
B+树由B树和索引顺序访问方法演化而来。B+树的定义十分复杂,这边值简要地介绍B+树;
B+树中,所有记录节点都是按键值的大小顺序存放在同一层的叶节点中,各叶节点指针进行连接。
所有记录都在叶节点中,并且是顺序存放的。
4.1 B+树的插入操作
B+树的插入必须保证插入后叶节点中的记录依然排序,同时需要考虑插入B+树的三种情况,每种情况都可能会导致不同的插入算法。
为了保持平衡,对于新插入的键值可能需要做大量的拆分页操作,而B+树主要用于磁盘,因此页的拆分意味着磁盘的操作,应该尽可能减少页的拆分。因此,B+树提供了旋转(rotation)的功能。
旋转发生在Leaf Page已经满了,但是其左右兄弟节点没有满的情况下。这时B+树并不急于去做拆分页的操作,而是将记录移到所在页的兄弟节点上。
4.2 B+树的删除操作
B+树使用填充因子(fill factor)来控制树的删除变换,50%是填充因子可设的最小值。
五、B+树索引
B+树索引本质就是B+树在数据库中的实现,但是B+索引在数据库中有一个特点就是高扇出性,因此在数据库中,B+树的高度一般都在23层,也就是说对于查找某一键值的行记录,最多只需要23次IO。
数据库的B+树索引可以分为聚集索引(clustered index)和辅助聚集索引(secondary index),聚集索引与非聚集索引不同的是,叶节点存放的是否是一整行的信息。
5.1 聚集索引
InnoDB存储引擎表示索引组织表,即表中数据按照主键顺序存放。而聚集索引就是按照每张表的主键构造一颗B+树,并且叶节点中存放着整张表的行记录数据,因此也让聚集索引的叶节点成为数据页。
由于实际的数据页只能按照一颗B+树进行排序,因此每张表只能有一个聚集索引。
聚集索引的存储并不是物理上的连续,而是逻辑上的连续。这其中有两点:一是页通过双向链表链接,页按照主键的顺序排序。另一点事每个页中的记录也是通过双向链表进行维护,物理存储上可以同样不按照主键索引。
聚集索引的好处是,它对于主键的排序查找和范围查询速度非常快。
5.2辅助索引
对于非聚集索引,叶级别不包含行的全部数据。叶节点除了包含键值以后,每个叶级别中的索引行还包含了一个书签(bookmark),该书签用来告诉InnoDB存储引擎,哪里可以找到与索引对应的行数据。因为InnoDB存储索引引擎表是索引组织表,因此InnoDB存储引擎的辅助索引的书签就是相应行数据的聚集索引键。
非聚集索引的存在并不影响数据在聚集索引中的组织,因此每张表上可以有多个辅助索引。当通过辅助索引来寻找数据时,InnoDB存储引擎会遍历辅助索引并通过叶级别的指针获得指向主键索引的主键,然后再通过主键索引来找到一个完整的行记录。
5.3 B+树索引的管理
索引的创建和删除可以通过两种方法,一种是ALTER TABLE,另一种是CREATE/DROP INDEX. ALTER TABLE创建索引的语法为:
ALTER TABLE tbl_name
| ADD (INDEX|KEY) {index_name}
[index_type] {index_col_name,...}
ALTER TABLE tbl_name
DROP PRIMARY KEY
| DROP {INDEX|KEY} index_name
CREATE [UNIQUE] INDEX index_name
[index_type]
ON tb1_name(index_col_name,...)
查看索引
SHOW INDEX;
添加联合索引
alter table t add key index_a_b (a,c);
分析sql
explain
6.2 顺序读、随机读与预读取
任何时候Why都比What重要,我已经告诉了你索引使用的原则,即高选择、取出表中少部分的数据。但是为什么只能是少部分的数据?在这之前先了解两个概念—顺序读与随机读。
顺序读(Sequntial Read)是指顺序地独处磁盘上的块(Block);随机读(Random Read)是指访问的块不是连续的,需要磁盘的磁头不断移动。
(我的理解:随机读并不是说意味上的那个随机,不是说想读啥读啥,而是意味着不是顺序读,需要磁头移动)
6.3 辅助索引的优化使用
辅助索引的叶节点包含有主键,但是辅助索引的叶并不包含完整的行信息。因此,InnoDB存储引擎总是会先从辅助索引的叶节点判断是否能得到所需的数据。
6.4 联合索引
什么时候需要使用联合索引呢?
先看一下联合索引内部的结构,从本质上来说,联合索引还是一颗B+树,不同的是联合索引的键值的数量不是1,而是大于等于2。
对于查询SELECT * FROM TABLE WHERE a=xxx and b=xxx
显然是可以使用(a,b)的这个联合索引。对于单个的a列查询
SELECT * FROM TABLE WHERE a=xxx也是可以使用这个(a,b)索引。但是对于b列的查询,SELEECT * FROM TABLE WHERE b=xxx,不可以使用这颗B+树索引。