最近面试php刚问,mysql的优化还是经常被问到的,索引从网上找些资料好好恶补一下。

MySQL 的索引

索引是一种用来实现 MySQL 高效获取数据的数据结构。

我们通常所说的在某个字段上建索引,意思就是让 MySQL 对该字段以索引这种数据结构来存储,然后查找的时候就有对应的查找算法。

建索引的根本目的是为了查找的优化,特别是当数据很庞大的时候,一般的查找算法有顺序查找、折半查找、快速查找等。

但是每种查找算法都只能应用于特定的数据结构之上,例如顺序查找依赖于顺序结构,折半查找通过二叉查找树或红黑树实现二分搜索。因此在数据之外,数据库系统还维护着满足特定查找算法的数据结构。

这些数据结构以某种方式引用数据,这样就可以在这些数据结构上实现高级查找算法,这种数据结构就是索引

1.数据结构相关知识:

二叉查找树:左子树的键值小于根的键值,右子树的键值大于根的键值。

 

AVL 树:平衡二叉树(AVL 树)在符合二叉查找树的条件下,还满足任何节点的两个子树的高度最大差为 1。

平衡多路查找树(B-Tree):为磁盘等外存储设备设计的一种平衡查找树。(节点的子树大于2)

系统从磁盘读取数据到内存时是以磁盘块(block)为基本单位的,位于同一磁盘块中的数据会被一次性读取出来,而不是按需读取

B+树:

(1)简介

B+树是应文件系统所需而产生的一种B树的变形树(文件的目录一级一级索引,只有最底层的叶子节点(文件)保存数据)非叶子节点只保存索引,不保存实际的数据,数据都保存在叶子节点中,这不就是文件系统文件的查找吗?

我们就举个文件查找的例子:有3个文件夹a、b、c, a包含b,b包含c,一个文件yang.c,a、b、c就是索引(存储在非叶子节点), a、b、c只是要找到的yang.c的key,而实际的数据yang.c存储在叶子节点上。

所有的非叶子节点都可以看成索引部分!

(2)B+树的性质(下面提到的都是和B树不相同的性质)

1、非叶子节点的子树指针与关键字个数相同; 
2、非叶子节点的子树指针p[i],指向关键字值属于[k[i],k[i+1]]的子树.(B树是开区间,也就是说B树不允许关键字重复,B+树允许重复); 
3、为所有叶子节点增加一个链指针; 
4、所有关键字都在叶子节点出现(稠密索引). (且链表中的关键字恰好是有序的); 
5、非叶子节点相当于是叶子节点的索引(稀疏索引),叶子节点相当于是存储(关键字)数据的数据层; 
6、更适合于文件系统;

 

MySQL索引实现

在MySQL中,索引属于存储引擎级别的概念,不同存储引擎对索引的实现方式是不同的,本文主要讨论MyISAM和InnoDB两个存储引擎的索引实现方式。

MyISAM索引实现

MyISAM引擎使用B+Tree作为索引结构,叶节点的data域存放的是数据记录的地址。下图是MyISAM索引的原理图:

 

innodb mysql 几个g 索引 mysql innodb索引原理_数据

innodb mysql 几个g 索引 mysql innodb索引原理_子节点_02

图8

这里设表一共有三列,假设我们以Col1为主键,则图8是一个MyISAM表的主索引(Primary key)示意。可以看出MyISAM的索引文件仅仅保存数据记录的地址。在MyISAM中,主索引和辅助索引(Secondary key)在结构上没有任何区别,只是主索引要求key是唯一的,而辅助索引的key可以重复。如果我们在Col2上建立一个辅助索引,则此索引的结构如下图所示:

innodb mysql 几个g 索引 mysql innodb索引原理_innodb mysql 几个g 索引_03

图9

同样也是一颗B+Tree,data域保存数据记录的地址。因此,MyISAM中索引检索的算法为首先按照B+Tree搜索算法搜索索引,如果指定的Key存在,则取出其data域的值,然后以data域的值为地址,读取相应数据记录。

MyISAM的索引方式也叫做“非聚集”的,之所以这么称呼是为了与InnoDB的聚集索引区分。

InnoDB索引实现

虽然InnoDB也使用B+Tree作为索引结构,但具体实现方式却与MyISAM截然不同。

第一个重大区别是InnoDB的数据文件本身就是索引文件。从上文知道,MyISAM索引文件和数据文件是分离的,索引文件仅保存数据记录的地址。而在InnoDB中,表数据文件本身就是按B+Tree组织的一个索引结构,这棵树的叶节点data域保存了完整的数据记录。这个索引的key是数据表的主键,因此InnoDB表数据文件本身就是主索引。

innodb mysql 几个g 索引 mysql innodb索引原理_innodb mysql 几个g 索引_04

图10

图10是InnoDB主索引(同时也是数据文件)的示意图,可以看到叶节点包含了完整的数据记录。这种索引叫做聚集索引。因为InnoDB的数据文件本身要按主键聚集,所以InnoDB要求表必须有主键(MyISAM可以没有),如果没有显式指定,则MySQL系统会自动选择一个可以唯一标识数据记录的列作为主键,如果不存在这种列,则MySQL自动为InnoDB表生成一个隐含字段作为主键,这个字段长度为6个字节,类型为长整形。

第二个与MyISAM索引的不同是InnoDB的辅助索引data域存储相应记录主键的值而不是地址。换句话说,InnoDB的所有辅助索引都引用主键作为data域。例如,图11为定义在Col3上的一个辅助索引:

innodb mysql 几个g 索引 mysql innodb索引原理_子节点_05

图11

这里以英文字符的ASCII码作为比较准则。聚集索引这种实现方式使得按主键的搜索十分高效,但是辅助索引搜索需要检索两遍索引:首先检索辅助索引获得主键,然后用主键到主索引中检索获得记录。

了解不同存储引擎的索引实现方式对于正确使用和优化索引都非常有帮助,例如知道了InnoDB的索引实现后,就很容易明白为什么不建议使用过长的字段作为主键,因为所有辅助索引都引用主索引,过长的主索引会令辅助索引变得过大。再例如,用非单调的字段作为主键在InnoDB中不是个好主意,因为InnoDB数据文件本身是一颗B+Tree,非单调的主键会造成在插入新记录时数据文件为了维持B+Tree的特性而频繁的分裂调整,十分低效,而使用自增字段作为主键则是一个很好的选择。

为什么说B+树比B树更适合数据库索引?

1、 B+树的磁盘读写代价更低:B+树的内部节点并没有指向关键字具体信息的指针,因此其内部节点相对B树更小,如果把所有同一内部节点的关键字存放在同一盘块中,那么盘块所能容纳的关键字数量也越多,一次性读入内存的需要查找的关键字也就越多,相对IO读写次数就降低了。

2、B+树的查询效率更加稳定:由于非终结点并不是最终指向文件内容的结点,而只是叶子结点中关键字的索引。所以任何关键字的查找必须走一条从根结点到叶子结点的路。所有关键字查询的路径长度相同,导致每一个数据的查询效率相当。

3、由于B+树的数据都存储在叶子结点中,分支结点均为索引,方便扫库,只需要扫一遍叶子结点即可,但是B树因为其分支结点同样存储着数据,我们要找到具体的数据,需要进行一次中序遍历按序来扫,所以B+树更加适合在区间查询的情况,所以通常B+树用于数据库索引。

联合索引的结构

其实联合索引的结构和上面是一样的,只是索引值是一个有序元祖<a1, a2, …, an>,根据查询条件,找到对应的索引节点,再向下找到对应的节点,最终获取到叶子节点的数据。

 

Inoodb行数据结构

innodb和大多数数据库一样,记录是以行的形式存储的。这意味只页(Innodb磁盘管理最小单位,一次读取一页的数据,通过innodb_page_size设置大小,默认16k)中保存着一行行的数据。而行记录有两种格式,5.1后默认行格式为Compact.

innodb mysql 几个g 索引 mysql innodb索引原理_子节点_06

第一个是记录非null变长字段长度列表, 按照字段列表逆序排列,即第一个值是最后一个变长字段的长度值。

第二个是值为null的列,例如列3的值为null,会在这里记录,但是后面的数据列不会显示

第三个记录头信息

第四..记录每个字段的值

还有两个隐藏列,记录事务ID和回滚指针列

行溢出数据

需要注意的是,我们创建表是 varchar(50),char(50),这个50指的是字符,而varchar总长度不能超过65535个字节 。但是innodb一个页最大是16k,16384个字节,如果超出就会发生行溢出,

innodb mysql 几个g 索引 mysql innodb索引原理_innodb mysql 几个g 索引_07

实际上innodb每个页至少要放2个行,如果放不下,就会自动把行数据放到溢出页(Blob page)中;而对于text和Blob类数据,也不一定就放到Blob page中,还是要判断一个页是否可以放两条记录。

 Innodb数据页结构

innodb mysql 几个g 索引 mysql innodb索引原理_innodb mysql 几个g 索引_08

1.File Header文件头存放了很多关于页的信息,比如FILE_PAGE_TYPE 标识页的类型(B+树叶子节点,Undo_log页,索引节点,BLOB页等) , FILE_PAGE_PRE,FILE_PAGE_NEXT 上一个和下一个页的指针。

2.Page Header: 保存页的状态信息,例如:当前页记录的数量,当前页属于哪个索引等