听说你很懂索引?
1. 知识前缀
索引有好几种数据结构:Hash表、B树、B+树等等(很懂的朋友可以跳到第二部分)
1.1 Hash表
当你的面前有数千万件杂乱无章的衣服时(假设每件衣服都有自己的编号)你现在想找某一编号的衣服,你需要从头开始一件件查看编号查找过去。哈希表可以理解成衣帽间中一串连在一起的小格子,你可以将这数千万件衣服的编号利用哈希算法得到不同的格子下标值。你以后需要找某件衣服的时候,按照同样的下标找到对应的格子,在格子中搜索你想要的衣服。这样一来就大大减少了搜索范围。
注意字段值所对应的数组下标是哈希算法随机算出来的,所以可能出现哈希冲突。
但是哈希表只能支持快速的精确查询,而不支持范围查询。
如果做成了索引,那速度也是很慢的,要全部扫描。
哈希表适合的场景:等值查询的场景,就只有KV(Key,Value)的情况,例如Redis、Memcached等这些NoSQL的中间件。
1.1.1题外话
既然Hash表是无序的,那为什么不适用有序的结构呢?
有序的数据结构就是有序数组,在等值查询和范围查询的时候表现都很好。有什么缺点呢?有序数组的缺点是不建议对有序数组进行增删改操作,每次修改都会对其他节点有影响。所以有序数组适用于静态数据的搜索,比如历史订单、历史记录等不会变动的历史数据。
1.2 B树
首先平衡二叉树不会有人不知道吧? 不会吧不会吧
B树是多路平衡查找树,B树中所有结点的孩子结点数的最大值称为B树的阶。
一颗m阶B树或为空树,或为满足如下特性的m叉树:
1)树中每个结点至多有m颗子树(即至多含有m-1个关键字)
2)若根节点不是终端结点,则至少有两颗子树
3)除根节点外的所有非叶结点至少有(m/2取上限)颗子树
具体的增删改查和具体的分裂操作,可以另外找资料学习一下先。。。
1.3 B+树
升级版B树
1)B+树的所有非叶子结点只存索引数据
2)B+树的叶子结点包含所有的索引值,并且指向数据
3)B+树的所有叶子结点相连
B+的优势?
其实很简单,我们看一下上面的数据结构,最开始的Hash不支持范围查询,二叉树树高很高,只有B树跟B+有的一比。
B树一个节点可以存储多个元素,相对于完全平衡二叉树整体的树高降低了,磁盘IO效率提高了。
而B+树是B树的升级版,只是把非叶子节点冗余一下,这么做的好处是为了提高范围查找的效率。
提高了的原因也无非是会有指针指向下一个节点的叶子节点。
2. 索引
2.1 什么是索引?我干嘛要用索引?
索引是一种特殊的文件(InnoDB 数据表上的索引是表空间的一个组成部分),它们 包含着对数据表里所有记录的引用指针。
2.2 为什么我加了索引还是慢?
select id from T where k = 5
select id,name ...from T where k = 5
select * from T where k =5
//这三行有何区别?为何?
第一种情况:这时候只需要查ID的值,而ID值已经在k索引树上,因此可以直接提供查询结果,不需要回表。也就是说,在这个查询里面,索引k已经覆盖了我们的查询需求,成为覆盖索引。
在这个过程中,回到主键索引树搜索的过程,我们称为回表。
第二种情况+第三种情况:在索引文件中没找到name时,还需要去找表文件,费时更长。
通常使用覆盖索引可以减少回表次数
2.3 为什么MySQL默认B+树而不是其他数据结构?
那么就一个个来讲咯
- 全部遍历一遍:这是最笨的方法,时间复杂度为O(n)
- 哈希表:优点是增删改查的时候O(1),缺点是范围查找排序查找O(n)
- 二叉树:O(log2(n)) 优点是深度N的节点查找次数为N ,缺点是:如果id持续递增树就非常不平衡O(n)
- 平衡二叉树:数据量越大树越高,导致磁盘IO过多
- B树:解决树高IO问题
- B+:在B树基础上,叶子节点可以递增搜索
2.4 啥玩意是最左前缀匹配原则?
最左匹配原则:
- 索引可以简单如一个列 (a),也可以复杂如多个列 (a,b,c,d),即联合索引。
- 如果是联合索引,那么key也由多个列组成,同时,索引只能用于查找key是否存在(相等),遇到范围查询 (>、<、between、like左匹配)等就不能进一步匹配了,后续退化为线性查找。
- 因此,列的排列顺序决定了可命中索引的列数。 例子:
如有索引 (a,b,c,d),查询条件 a=1 and b=2 and c>3 and d=4,则会在每个节点依次命中a、b、c,无法命中d。(c已经是范围查询了,d肯定是排不了序了)
2.5思考B+树中一个节点到底多大合适?
B+树中一个节点为一页或页的倍数最为合适,因为如果一个节点的大小小于1页,那么读取这个节点的时候其实也会读出1页,造成资源的浪费。
如果一个节点的大小大于1页,比如1.2页,那么读取这个节点的时候会读出2页,也会造成资源的浪费。
所以为了不造成浪费,所以最后把一个节点的大小控制在1页、2页、3页、4页等倍数页大小最为合适。
首先Mysql的基本存储结构是页(记录都存在页里边): 各个数据页可以组成一个双向链表 而每个数据页中的记录又可以组成一个单向链表 每个数据页都会为存储在它里边儿的记录生成一个页目录,在通过主键查找某条记录的时候可以在页目录中使用二分法快速定位到对应的槽,然后再遍历该槽对应分组中的记录即可快速找到指定的记录 以其他列(非主键)作为搜索条件:只能从最小记录开始依次遍历单链表中的每条记录。 所以说,如果我们写 select * from user where username='QIANFUDEAI'这样没有进行任何优化的sql语句,默认会这样做:
- 定位到记录所在的页
- 需要遍历双向链表,找到所在的页
- 从所在的页内中查找相应的记录
- 由于不是根据主键查询,只能遍历所在页的单链表了
参考资料:
https://segmentfault.com/a/11...
《高性能MySQL》
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。