一、索引:
1. 索引的概念:
索引是帮助Mysql高效获取数据的排好序的数据结构
2. 索引存储在文件里:
mysql主要有两种存储引擎: Myisam、Innodb两种
对于存储引擎为Myisam的数据表中,有三种文件格式,以.frm为后缀的表结构文件、以MYD为后缀的数据文件,以MYI为后缀的索引文件;
对于存储引擎为Innodb的数据表中,有两种文件格式,以.frm为后缀的表结构文件、以ibd为后缀的索引与数据合并的文件
myisam索引实现: 非聚集索引(索引文件和数据文件是分离的)
Innodb索引(聚集索引)
二、索引的各种存储结构及其优缺点
在开始讲这一小节之前,我们先来看一下在数据库没有加索引的情况下,SQL中的where字句是如何查找目标记录的。
我们先看下左边表格第二列Col2列的数据时如何查找的,如果我们希望查找where Col2 = 22的记录,我们在没加索引的情况下是按顺序从第一条记录查找,由此可知需要查找5次才能找到;
如果对Col2字段加上索引后,我们假设使用最简单的二叉树作为索引存储方式,再次查找where Col2 = 22的记录这次只需要查找2次就能找到目标记录,效率提高十分明显。
1. 二叉树
二叉树是一种比顺序结构更加高效的查找目标元素的结构,它可以从第一个父节点开始跟目标元素值比较,如果相等则返回当前节点,如果目标元素小于当前节点,则移动到左侧子节点进行比较,
大于的情况则移动到右侧子节点进行比较,反复进行操作最终移动到目标元素节点的位置
二叉树缺点:
在大部分情况下,我们设计索引时,都会在表中提供一个自增索引字段作为建立索引的列,这种情况下所有的只能增节点都会添加到上一节点的右侧,
这种情况下查询节点时跟没有添加索引的情况是一样的。
. 红黑树
红黑树也叫平衡二叉树,它继承了平衡二叉树的优点,同时也解决了上面二叉树自增整形索引的问题,从下面的动态图可以看出红黑树会不断对树结构进行调整
红黑树的缺点:
在数据量大的时候,树的高度也很大,从图中可以看出每个节点只能有两个子节点,所以红黑树的高度会达到几十以上,增加了磁盘的I/O,降低了查找的效率。
3. HASH
对数据进行Hash散列运算,通过Hash算法计算hash值,再根据hash值从索引文件中获取文件在磁盘的位置并读取,快速定位目标记录,查询效率高。
缺点:
无法解决范围查询的场景、也不支持模糊查询。
4. B-Tree
既然红黑树存在缺点,那么我们可以在红黑树的基础之上构造一种新的存储结构,解决思路:因为树的高度太大,就只需要适当的增加每个树节点可以存储的数据个数即可,
但是数据个数也需要有一个阈值,否则会导致一个节点的数据过多导致其他的问题。
先来了解一下B-Tree的知识点
度(degree)-节点的数据存储个数,每个树节点中数据的个数大于15/16 * degree时会自动分裂,调整结构。
叶节点具有相同的深度
叶子节点的指针为空
节点中的数据key从左到右递增排列
1. 树节点结构:
在这里需要说明下的是,BTree的结构里每个节点包含了索引值和表记录的信息,我们可以按照Map集合这样理解:key=索引,value=表记录,如下图所示:
2. 优点:
BTree的结构可以弥补红黑树的缺点,解决数据量过大时整棵树高度过大的问题,相同的数据量只需要更少的层,相同的深度可以存储更多的数据,查找效率更高。
3. 缺点
在查询单条数据是很快的,但是如果范围查询的话,BTree结构每次都需要从根节点查询一遍,会影响效率,因此在实际应用时采用的是B+Tree
5. B+Tree(MySQL索引的真正存储结构)
在介绍B+Tree之前,我们先来看下面两个问题:
1. 为什么要对BTree继续做优化?
要解答这个疑问需要先了解BTree每个节点结构(上面已经说明)和MySQL数据库它是如何读取索引数据的,索引和表数据在不使用的时候是存储在文件中的,也就是磁盘,
当我们执行查询操作时DBMS(数据库管理系统)首先会先从内存中查找,如果找到直接使用,如果找不到则从磁盘文件中读取;操作系统储存数据的最小单位是页(page),
innodb引擎默认的innodb_page_size为16K,对内存和磁盘读取数据是按一页的整数倍读取的。
这里我们假设数据库一次IO操作就读取1页16K的数据,再假设图中圈起来的元素就是一个大节点,内含多个小节点的索引和数据,其大小是10MB,
那么我们要从磁盘中读取完整个大节点需要进行 10M / 16K = 600多次IO操作,这样就可以看出如果大节点数据总量越大,需要执行的IO操作越多,花费的时间也越长,
因此为了提高性能,数据库会建议一个大节点只存储一页16K大小的数据,这里的数据包含了索引和表记录,另外我们还能计算出树的度Degree应该设置成多大才合理:
Degree = 内存页大小(16K) / 单个索引值字节大小;
进一步分析,索引值的大小相对于整条记录的大小是很小的,如果我们需要查找的数据刚好是在最后,那么前面遍历过的节点中存储的记录数据是不是对我们来说是没用的,
它会占用比索引大得多的空间,导致我们一个大节点里能遍历的索引数量大大减少,需要向下继续遍历的几率就更大,花费更多时间查找,那么有没有办法可以优化呢?看下一个问题。
2. 相对于BTree,B+Tree做了哪些优化?
B+Tree存储结构,只有叶子节点存储数据
新的B+树结构没有在所有的节点里存储记录数据,而是只在最下层的叶子节点存储,上层的所有非叶子节点只存放索引信息,
这样的结构可以让单个节点存放下更多索引值,增大度Degree的值,提高命中目标记录的几率。
这种结构会在上层非叶子节点存储一部分冗余数据,但是这样的缺点都是可以容忍的,因为冗余的都是索引数据,不会对内存造成大的负担。
3. 统计B+tree结构中两层,三层分别可以存储的记录数.
B+tree的非叶子节点存储的是索引值,还有指针: 索引值一般使用的是bigint类型,占8byte,指针占用6byte,所以一页可以存放:16kb / 14byte = 1170个索引值
B+tree叶子节点存储的是数据,假设每条数据记录占用1kb.
所以两层的B+tree数可以存放:16kb / 1kb * 1170 = 18720条数据,将近两万条数据
所以三层的B+tree数可以存放:16kb / 1kb * 1170 * 1170 = 21902400条数据,2000多万条数据.
B+Tree特性:
非叶子节点不存储data,只存储key(索引值),可增大度
叶子节点不存储指针
顺序访问指针,提高区间访问的性能
每个叶子节点都指向下一个叶子节点
这点优化有什么用呢?我们直接看下面的B+Tree结构,如果我们进行范围查找where id > 4的记录,我们只需要先找到id = 4的记录后自然就能通过叶子节点间的双向指针方便地查询出大于4的所有记录。
三. 联合索引底层存储结构
单列索引其实也可以看做联合索引,索引列为1的联合索引,从下图就可以看出联合索引的底层存储跟单列索引时类似的,区别在于联合索引是每个树节点中包含多个索引值,
在通过索引查找记录时,会先将联合索引中第一个索引列与节点中第一个索引值进行匹配,匹配成功接着匹配第二个索引列和索引值,直到联合索引的所有索引列都匹配完;
如果过程中出现某一个索引列与节点相应位置的索引值不匹配的情况,则无需再匹配节点中剩余索引列,前往下一个节点。