索引的本质
【意】 索引是帮助MySql高效的获取数据的排好序的数据结构。
数据结构
二叉树
高度大,查询慢,会变成链表
红黑树
也叫二叉平衡树,自动平衡数据。
但是高度不可控,查询慢
B-Tree
也叫多路平衡二叉树
在进行范围查找的时候,不能快速的查到数据

B+Tree(B-Tree变种)
不是叶子节点不储存数据,值储存索引,这样就可以放更多的索引。
每个索引旁边又有一个指针指向下一个节点,这个节点也存放了上一节点的索引。
这个指针指向一个范围子节点区,每个范围子节点都有一个双向指针,
这个双向指针左边指向小的范围区,右边指向大的范围区。
也就是说,这个范围区的双向指针可以进行范围查询。

MyISAM数据库引擎
索引与数据分开保存,B+Tree只存索引,也就是非聚合索引
性能好,但是没有事务。
InnoDB数据库引擎
索引与数据都放在B+Tree叶子节点,就是聚合索引
必须有主键,并且推荐是整型,如果是String类型主键可能会变成4层节点,并且需要判断大小,浪费时间。
有事务,回滚,奔溃修复能力,与外部键
HashMap
java7是数组+链表
java8是数组+链表+红黑树,当链表的长度>=8就会转成红黑树
通过hash%数组长度获得index
通过:index = hash%length
如果出现了相同的hash值,那就会出现hash碰撞,当出现碰撞之后,一个数组下标就会储存两个元素,这两个元素就会变成链表。
但是链表的查询速度很慢(O(n)),所以当链表的长度>=8就会转成红黑树。
数组长度
数组长度=定义的长度<=2的指数次幂
也就是大于或等于定义长度的2^n,其中最接近的数就是数字长度

用&来计算索引的下标位置,因为&计算的是二进制的。
&运算结果是: 两个同时为1,结果为1,否则为0

就比如默认初始是16的大小,长度-1就是15。
这时又一串hashcode:1101 0010 1010 0110
15的长度变成二进制: 0000 0000 0000 1111
当进行&运算变成了: 0000 0000 0000 0110
最后下标是:6
这样保证元素位置在0-15之间,并且速度快。
为什么加载因子是0.75?
加载因子是表示Hash表中元素填满的长度。
诺加载叶子越大,填满的元素就越多,好处是空间利用率高了,但hash冲突的机会加大了,反之加载因子越小填满的元素越少,好处是冲突减小,空间浪费多了。
冲突的机会越大,则查找的成本越高.反之,查找的成本越小.因而,查找时间就越小.
因此,必须在 "冲突的机会"与"空间利用率"之间寻找一种平衡与折衷. 这种平衡与折衷本质上是数据结构中有名的"时-空"矛盾的平衡与折衷.
所有的Hash表都理论上都不可能避免冲突(无论Hash函数如何设计).当表中都快填满时(加载因子大),再填入新的元素时,冲突的机会将很大.
JAVA的HashMap当put(…)时若产生冲突(即:两个不同的对象,但其hadhCode相同,表明想占用表中同一个位置空间),则目前的实现中是:占用Hash表中同一个位置空间的冲突的元素,用一个链表来链起来.这样:当get(…)时,若有冲突,就要访问链表了(这样,一旦有冲突,put(…)与get(…)都要和链表打交道,成本当然就高了)
因此:要尽最大努力,减少冲突的机会
https://www.baidu.com/link?url=pvL5lkGKu1oX1Z8hjn8V5AjvXLt6GOmYqarkwtWIKwkWpfYYJd8ASt4bbVb0pLQNupHkwR9NNv_PqVS7Ftvp3Abq1AMSd8Pnc8Rf_oPHOhK&wd=&eqid=8631fe7800098d1e000000065ec33bed
为什么链表长度到达8就变成红黑树?
泊松分布
是一种概率学,未来可以会发生的事情

所以得出,链表的长度越大,发生hash碰撞的概率就越低,到了长度8碰撞就概率就接近于0.
所以链表转成红黑树概率也小,只有在这个hash表的数据非常大的时候才会转成红黑树。
JDK7的hash出现环型链表
在put值后,会进入addEntry进行判断是否需要扩容,
如果我这个大小,大于等于我计算的元素填充长度(16-1)* 0.75 = 12, 并且这个元素插入的下标不为空

那环型链表就是在扩容时出现的。
因为扩容会进行一个迁移,把旧数组迁移到新数组。
移位

迁移后的顺序是反的。
先e指向的第一个元素移到新的数组,再让下一个元素的next指向当前元素,到时移位的时候,下一个元素的next就是它上一个元素的。所以顺序反了。
但是如果是多线程就会变成环。
比如第一个线程进来但是阻塞后,第二个线程完成了迁移。
但是第一个线程有指向数组的指针,这时的指针e还是第一个元素,而next时第二个元素。
第一个线程有又会重新进行迁移.
那就变成了e=元素1,next=null
再次循环判断e不为空,e.next=元素1,e=元素2。
再次判断又变成e=元素1,e.next=null,然后结束循环。
但是读取时就会出现环型链路
Java8hashmap
并且没有重新hash,速度快。
会重新进行迁移.
那就变成了e=元素1,next=null
再次循环判断e不为空,e.next=元素1,e=元素2。
再次判断又变成e=元素1,e.next=null,然后结束循环。
但是读取时就会出现环型链路
Java8hashmap
并且没有重新hash,速度快。
出现一个地位指针和高位指针,会把链表拆分,并且保存到其他数组节点,而不是原来的节点

946

被折叠的 条评论
为什么被折叠?



