越努力,越幸运,
本文已收藏在GitHub中 JavaCommunity, 里面有面试分享、源码分析系列文章,欢迎收藏,点赞
https://github.com/Ccww-lx/JavaCommunity
数据库索引在平时的工做是必备的,怎么建索引,怎么使用索引,能够提升数据的查询效率。并且在面试过程,数据库的索引也是必问的知识点,好比:git
索引的类型github
看着这些,能说出多少,理解多少呢?所以咱们须要去探究其内在原理。面试
索引的目的为了加速检索数据而设计的一种分散存储(索引经常很大,属于硬盘级的东西,因此是分散存储)的数据结构,其原理以空间换时间。
而快速检索的实现的本质是数据结构,经过不一样数据结构的选择,实现各类数据快速检索,索引有哈希索引和B+树索引。算法
数据库索引底层选型归根到底就是为提升检索效率,那么就须要考虑几个问题:sql
NOTE: 考虑到磁盘IO是很是高昂的操做,计算机操做系统作了一些优化,当一次IO时,不光把当前磁盘地址的数据,而是把相邻的数据也都读取到内存缓冲区内,由于局部预读性原理告诉咱们,当计算机访问一个地址的数据的时候,与其相邻的数据也会很快被访问到。每一次IO读取的数据咱们称之为一页(page)。
哈希表是根据键(Key)而直接访问在内存存储位置的数据结构。数据库
经过计算一个关于键值的函数,将所需查询的数据映射到表中一个位置来访问记录,这加快了查找速度。虽然查询时间复杂度为O(1),但存在着碰撞问题,最坏状况会致使时间复杂急剧增长;微信
并且哈希表其只适合精准key(等于)检索,不适合范围式检索,范围检索就须要一次把全部数据找出来加载到内存,没有效率,所以不适合Mysql的底层索引的数据结构。数据结构
为了优化高效范围查询,且时间复杂度小,引入二叉查找树函数
二叉查找树的时间复杂度是 O(lgn),因为数据已排序好了,因此范围查询是能够高效查询,源码分析
但会存在的问题:左右子节点的深度可能相差很大,最极端的状况只有左子树或者右子树,此时查找的效率为O(n),检索性能急剧降低,所以也不适合Mysql的底层索引的数据结构。
为了优化二叉树左右子树深度相差太大的问题,咱们引入了平衡二叉树,即左右子节点的深度差不超过1
平衡二叉树看来好像适合,能够实现:
NOTE:上图中一个磁盘块,表明硬盘上的一个存储位置
可是咱们还有一个最重要因素须要考虑,磁盘IO与预读,且数据库查询数据的瓶颈在于磁盘 IO,使用平衡二叉树根据索引进行查找时,每读一个磁盘块就进行一次IO,这样没有实现计算机的预读,致使检索效率,总结出平衡二叉树做为索引的问题(上图中一个磁盘块,表明硬盘上的一个存储位置):
4K
,Mysql一次IO 16K
,而图上的磁盘块能明显达不到4K为了优化磁盘IO和预读,减小IO操做,条路太少了,那么换成多条路,那么会想到使用B树和B+树,但B树每一个节点限制最多存储两个 key,也会形成IO操做过于频繁,所以优化思路为:尽量在一次磁盘 IO 中多读一点数据到内存,那么B+树也该出场:
相对于B树,B+树的优点有:
B+树扫库扫表的能力更强
索引在不一样的存储引擎中体现形式步同样, 最多见的是:
InnoDB存储引擎中,索引和数据是存放在同一个文件的,属于汇集索引 。并且InnoDB会自动创建好主键 ID 索引树, 所以建表时要求必须指定主键的缘由。
其中,主键索引(汇集索引)的叶子节点记录了数据,而不是数据的物理地址。辅助索引的叶子节点存放的是主键key。因此当利用辅助索引查找数据时,实际上查了两遍索引(辅助索引和主键索引):
Myisam存储引擎中,索引和数据是存放在两个文件中的,属于非汇集索引 。不论是主键索引仍是辅助索引,其叶子节点都是记录了数据的物理地址。
MySQL索引能够分为:
惟一索引:
联合索引:
其中,主要理解一下联合索引的问题,存储结构,查询方式。
联合索引,多个列组成的索引叫作联合索引,单列索引是特殊的联合索引。其存储结构以下:
<font color='red'>对于联合索引来讲其存储结构只不过比单值索引多了几列,组合索引列数据都记录在索引树上,(不一样的组合索引,B+树也是不一样的),且存储引擎会首先根据第一个索引列排序后,其余列再依次将相等值的进行排序。</font>
NOTE:叶节点第一排,按顺序排序好,第二列,会基于第一列排序好的,将第一列相等的再下一列再排序,依次类推。
<font color='red'>联合索引查询方式,存储引擎首先从根节点(通常常驻内存)开始查找,而后再依次在其余列中查询,直到找到该索引下的data元素即ID值,再从主键索引树上找到最终数据。</font>
并且联合索引其选择的原则:
最左前缀匹配原则和联合索引的索引构建方式及存储结构是有关系的。根据上述理解分析,能够得出联合索引只能从多列索引的第一列开始查找索引才会生效,好比:
假设表user上有个联合索引(a,b,c),那么 select * from user where b = 1 and c = 2将不会命中索引缘由是联合索引的是存储引擎先按第一个字段排序,再按第二个字段排序,依次排序。
当索引中的一列离散度太低时,优化器可能直接不走索引,离散度计算方法:
离散度 = 列中不重复的数据量 / 这一列的总数据量
若是一个索引包含(或覆盖)全部须要查询的字段的值,称为覆盖索,即只需扫描索引而无须回表查询 。覆盖索引可减小数据库IO,将随机IO变为顺序IO,可提升查询性能。
对于InnoDB辅助索引在叶子节点中保存了行的主键值,因此若是辅助索引(包括联合索引)可以覆盖查询,则能够避免对主键索引的二次查询。好比:
--建立联合索引 create index name_phone_idx on user(name,phoneNum); --此时是覆盖索引,缘由是根据name来查,命中索引name_phone_idx, --其关键字为name,phoneNum,自己就已经包含了查询的列。 select name,phoneNum where name = "张三"; --若是id为主键的话,此时也称做覆盖索引,缘由:辅助索引的叶子节点存的就是主键 select id,name,phoneNum where name = "张三";
MySQL的索引有不少知识点要掌握,已学习了索引的底层存储结构,不一样存储引擎中的索引体现,以及索引类型的基础原理知识分析,能够为后续的数据库优化提供理论知识的支撑,也会更好的理解优化方案。后续会有优化篇章
谢谢各位点赞,没点赞的点个赞支持支持 最后,微信搜《Ccww技术博客》观看更多文章,也欢迎关注一波。