MYSQL索引底层原理
1. 索引的本质
索引是帮助MYSQL高效获取数据的排好序的数据结构。
索引一般以文件形式存储在磁盘上。
2. 按索引的分类
2.1. 按字段约束分类
2.1.1. 普通索引
是最基本的索引,它没有任何限制。
ALTER TABLE 'table_name' ADD INDEX index_name('col');
CREATE INDEX index_name ON table_name('col');
2.1.2. 主键索引(PRIMARY)
即主索引,是一种特殊的唯一索引,根据主键建立,一个表只能有一个主键索引,不允许重复,不允许有空值。
ALTER TABLE 'table_name' ADD PRIMARY KEY pk_index('col');
2.1.3. 唯一索引(UNIQUE)
用来建立索引的列的值必须是唯一的,允许空值。如果是组合索引,则列值的组合必须唯一。
ALTER TABLE 'table_name' ADD UNIQUE INDEX index_name('col');
2.1.4. 全文索引(FULLTEXT)
主要用来查找文本中的关键字,而不是直接与索引中的值相比较。全文索引跟其它索引大不相同,它更像是一个搜索引擎,而不是简单的参数匹配。
用于char、varchar、text列上。对于文本的大对象,或者较大的char或vachar类型的数据,如果用普通索引,那么匹配文本中间的单词,如果用like需要很长的时间来处理,相应时间会大大增加。这种情况,就可使用全文索引,在生成全文索引时,会为文本生成一份单词的清单,在索引时会根据这个单词清单来索引。
在数据量较大时,先将数据插入表中,再给表创建全文索引,要比先建一个有全文索引的表,再插入数据的速度快很多。
全文索引配合match against操作使用,而不是用like语句。
ALTER TABLE 'table_name' ADD FULLTEXT INDEX ft_index('col');
SELECT * FROM table_name WHERE MATCH (列名) AGAINST ('查询字符串' IN NATURAL LANGUAGE MODE);
2.1.5. 组合索引
用多个列组合构建的索引,遵循“最左前缀原则”,把最常用作为检索或排序的列放在最左边,依次递减。其效率大于索引合并。
ALTER TABLE 'table_name' ADD INDEX index_name('col1','col2','col3');
在使用组合索引的时候可能因为列名长度过长而导致索引的key太大,导致效率降低,在允许的情况下,可以只取列的前几个字符作为索引。
ALTER TABLE 'table_name' ADD INDEX index_name(col1(4), col2(3));
2.2. 按索引与数据的存储关联性分类
2.2.1. 聚簇索引
Innodb中的主键索引(B+树索引)结构中,非叶子节点存储的是索引指针,叶子节点存储的是既有索引也有整行数据。索引和数据是存储在一起的,是典型的聚簇索引。
2.2.2. 非聚簇索引
InnoDB中的辅助索引结构,叶子节点存储的是主键索引值,并没有完整数据,所以为非聚簇索引。
MyISAM中索引和数据文件分开存储,B+Tree的叶子节点存储的是数据存放的地址,而不是具体的数据,是典型的非聚簇索引;换言之,数据可以在磁盘上随便找地方存,索引也可以在磁盘上随便找地方存,只要叶子节点记录对了数据存放地址就行。因此,索引存储顺序和数据存储关系毫无关联,是典型的非聚簇索引。
3. 索引数据结构
3.1. 数据结构网站
数据结构网站
3.2. 二叉树
二叉树是指树中节点的叶子不大于2的有序树,它是一种最简单且最重要的树。二叉树的递归定义为:二叉树是一棵空树,或者是一棵由一个根节点和两棵互不相交的,分别称作根的左子树和右子树组成的非空树;左子树和右子树又同样都是二叉树。
缺点:递增的列(如:id),单边增长数据列,退化成了链表结构。
3.3. 红黑树
红黑树是一种平衡二叉查找树的变体,它的左右子树高差有可能大于 1,所以红黑树不是严格意义上的平衡二叉树(AVL),但对之进行平衡的代价较低, 其平均统计性能要强于AVL 。
当两边层数相差大了,会自动平衡,保证两边层数不要相差过多。
缺点:大数据量时,树的层数过高,导致查询效率低。
3.4. Hash表
Hash表也叫散列表,是根据关键码值直接进行访问的数据结构。也就是说,它通过把关键码值映射到表中一个位置来访问记录,以加快查找的速度。这个映射函数叫做散列函数,存放记录的数组叫做散列表。精确查找很快。
缺点:
1> 利用hash存储的话需要将所有的数据文件添加到内存,比较耗费内存空间。
2> 无法支持范围查找,无法支持排序,以及无法支持部分模糊查询。
3> 不支持多列联合索引。
4> 如果Hash碰撞过多,也会成链表结构,索引效率会降低。
3.5. B-Tree
B树是一种自平衡的树,能够保持数据有序。这种数据结构能够让查找数据、顺序访问、插入数据及删除的动作,都在对数时间内完成。
特点:
1>叶节点具有相同的深度,叶节点的指针为空。
2>所有索引元素不重复;
3>节点中的数据索引从左到右递增排列。
4>每个节点都有data。
缺点:
1>如果data过大,一个节点存放的数据少,这样会导致树加深,这样也增加了IO次数。
2>范围查询支持不好。
3.6. B+Tree
B+树是B树的一种变形形式,B+树上的叶子结点存储关键字以及相应记录或记录的地址,叶子结点以上各层作为索引使用。
B+树的查找与B树不同,当索引部分某个结点的关键字与所查的关键字相等时,并不停止查找,应继续沿着这个关键字右边的指针向下,一直查到该关键字所在的叶子结点为止。
特点:
1>非叶子节点不存储data,只存储索引,可以放更多的索引。
2>叶子节点包含所有索引字段。
3>叶子节点用指针连接,提高区间访问的性能。
优点:
1>较B-Tree,横向存储的索引更多,这样可大量减少磁盘I/O的次数。
2>能很好的支持范围查询,因为叶子节点之间有双向指针连接。
4. 预备知识
4.1. 局部性原理
局部性原理是指CPU访问存储器时,无论是存取指令还是存取数据,所访问的存储单元都趋于聚集在一个较小的连续区域中。
当一个数据被用到时,其附近的数据也通常会马上被使用。
4.2. 磁盘预读原理
由于存储介质的特性,磁盘本身存取就比主存慢很多,再加上机械运动耗费,磁盘的存取速度往往是主存的几百分之一,因此为了提高效率,要尽量减少磁盘I/O。为了达到这个目的,磁盘往往不是严格按需读取,而是每次都会预读,即使只需要一个字节,磁盘也会从这个位置开始,顺序向后读取一定长度的数据放入内存。
由于磁盘顺序读取的效率很高(不需要寻道时间,只需很少的旋转时间),因此对于具有局部性的程序来说,预读可以提高I/O效率。
预读的长度一般为页(page)的整倍数。页是计算机管理存储器的逻辑块,硬件及操作系统往往将主存和磁盘存储区分割为连续的大小相等的块,每个存 储块称为一页(在许多操作系统中,页得大小通常为4k),主存和磁盘以页为单位交换数据。当程序要读取的数据不在主存中时,会触发一个缺页异常,此时系统 会向磁盘发出读盘信号,磁盘会找到数据的起始位置并向后连续读取一页或几页载入内存中。
4.3. 数据页
数据页是mysql中磁盘和内存交换的基本单位,也是mysql管理存储空间的基本单位。
同一个数据库实例的所有表空间都有相同的页大小;默认情况下,表空间中的页大小都为 16KB,当然也可以通过改变 innodb_page_size 选项对默认大小进行修改,一般都是操作系统的整数倍。
一次最少从磁盘读取16KB内容到内存中,一次最少把内存中16KB内容刷新到磁盘中。
名称 |
中文名 |
占用空间大小 |
简单描述 |
File Header |
文件头部 |
38字节 |
页的一些通用信息 |
Page Header |
页面头部 |
56字节 |
数据页专有的一些信息 |
Infimum + Supremum |
最小记录和最大记录 |
26字节 |
两个虚拟的行记录 |
User Records |
用户记录 |
大小不确定 |
实际存储的行记录内容 |
Free Space |
空闲空间 |
大小不确定 |
页中尚未使用的空间 |
Page Directory |
页面目录 |
大小不确定 |
页中的某些记录的相对位置 |
File Trailer |
文件尾部 |
8字节 |
检验页是否完整 |
5. InnoDB的索引实现
InnoDB的主键索引是聚簇索引,叶子节点包含了完整的数据记录,数据和索引在一起,存放在同一个文件中(XXX.ibd)。
一张表有且仅有一个聚簇索引。
InnoDB的非主键索引是非聚簇索引,叶子节点没有数据记录。
5.1. 主键索引
如果一张表没有主键,mysql会找一列unique索引作为聚簇索引来组织数据。如果没有这样的索引,mysql会生成一个唯一的隐藏列,类似于rowid,用这个列生成聚簇索引,从而组织数据。
主键列推荐用自增整型,这样生成树的时候,直接往后添加,不需要调整排序;而且越小越好,这样一页放的数据量更多,查询更快。整型查询比较大小时,速度更快。
一页存放的主键个数:16kb/(8+6)byte=1170个,其中假设主键是bigint占8byte,指针占6byte
叶子节点存放的数据条数:16kb/1kb=16行数据,其他假设一行数据占1kb,一页可存16行数据
B+树层数为2可存的数据条数:1170*16=18720
B+树层数为3可存的数据条数:1170*1170*16=21902400(2千万条数据)
5.2. 非主键索引
非主键索引,也可以叫二级索引、辅助索引,它属于非聚簇索引,即索引和数据是分开存放的。
InnoDB的非主键索引的叶子节点没有存储整行数据,而是存储的主键值,这样既能保证数据一致,也能节省存储空间。
这样的设计,导致的一个问题是,当查询用到了非主键索引,查到主键之后,还需要通过主键到主键索引中做回表查询,从而得到数据。所以并不是所有情况都会走这个非主键索引,如果回表次数过多,则会放弃使用该索引,直接进行全表扫描。
6. MyISAM的索引实现
6.1. MyISAM的索引
MyISAM引擎同InnoDB一样,也是使用B+Tree作为索引的数据结构,不同的是MyISAM索引的叶节点data域中存放的是数据记录的地址。MyISAM的索引方式叫做“非聚簇索引”。
因此,MyISAM中索引检索的算法为首先按照B+Tree搜索算法搜索索引,如果指定的Key存在,则取出其data域的值,然后以data域的值为地址,读取相应数据记录。
6.1.1. 主键索引
MyISAM可以没有主键索引。
6.1.2. 非主键索引
在MyISAM中,主键索引和非主键索引在结构上没有任何区别,只是主键索引要求key是唯一的,而非主键索引的key是可以重复的。