Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 22 additions & 5 deletions docs/database/MySQL数据库索引.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,20 +22,37 @@

## 索引的底层数据结构

### Hash & B+树
### Hash表 & B+树

Hash 索引指的就是 Hash 表,最大的优点就是能够在很短的时间内,根据 Hash 函数定位到数据所在的位置,也就是说 Hash 索引检索指定数据的时间复杂度可以接近 0(1)。
哈希表是键值对的集合,通过键(key)即可快速取出对应的值(value),因此哈希表可以快速检索数据(接近 O(1))。

但是,MySQL 并没有使用 Hash 索引而是使用 B+树作为索引的数据结构。**为什么呢?**
**为何能够通过 key 快速取出 value呢?** 原因在于 **哈希算法**(也叫散列算法)。通过哈希算法,我们可以快速找到 value 对应的 index,找到了 index 也就找到了对应的 value。

**1.Hash 冲突问题** :知道 HashMap 或 HashTable 的同学,相信都知道它们最大的缺点就是 Hash 冲突了。不过对于数据库来说这还不算最大的缺点。
```java
hash = hashfunc(key)
index = hash % array_size
```



![](https://img-blog.csdnimg.cn/20210513092328171.png)

但是!哈希算法有个 **Hash 冲突** 问题,也就是说多个不同的 key 最后得到的 index 相同。通常情况下,我们常用的解决办法是 **链地址法**。链地址法就是将哈希冲突数据存放在链表中。就比如 JDK1.8 之前 `HashMap` 就是通过链地址法来解决哈希冲突的。不过,JDK1.8 以后`HashMap`为了减少链表过长的时候搜索时间过长引入了红黑树。

![](https://img-blog.csdnimg.cn/20210513092224836.png)

为了减少 Hash 冲突的发生,一个好的哈希函数应该“均匀地”将数据分布在整个可能的哈希值集合中。

既然哈希表这么快,**为什么MySQL 没有使用其作为索引的数据结构呢?**

**1.Hash 冲突问题** :我们上面也提到过Hash 冲突了,不过对于数据库来说这还不算最大的缺点。

**2.Hash 索引不支持顺序和范围查询(Hash 索引不支持顺序和范围查询是它最大的缺点:** 假如我们要对表中的数据进行排序或者进行范围查询,那 Hash 索引可就不行了。

试想一种情况:

```java
SELECT * FROM tb1 WHERE id < 500;
SELECT * FROM tb1 WHERE id < 500;Copy to clipboardErrorCopied
```

在这种范围查询中,优势非常大,直接遍历比 500 小的叶子节点就够了。而 Hash 索引是根据 hash 算法来定位的,难不成还要把 1 - 499 的数据,每个都进行一次 hash 计算来定位吗?这就是 Hash 最大的缺点了。
Expand Down
2 changes: 1 addition & 1 deletion docs/java/basis/Java基础知识.md
Original file line number Diff line number Diff line change
Expand Up @@ -668,7 +668,7 @@ Integer i2 = new Integer(40);
System.out.println(i1==i2);
```

`Integer i1=40` 这一行代码会发生拆箱,也就是说这行代码等价于 `Integer i1=Integer.valueOf(40)` 。因此,`i1` 直接使用的是常量池中的对象。而`Integer i1 = new Integer(40)` 会直接创建新的对象。
`Integer i1=40` 这一行代码会发生装箱,也就是说这行代码等价于 `Integer i1=Integer.valueOf(40)` 。因此,`i1` 直接使用的是常量池中的对象。而`Integer i1 = new Integer(40)` 会直接创建新的对象。

因此,答案是 `false` 。你答对了吗?

Expand Down
2 changes: 1 addition & 1 deletion docs/java/basis/Java基础知识疑难点.md
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,7 @@ System.out.println(a);// 0.100000024
System.out.println(b);// 0.099999964
System.out.println(a == b);// false
```
具有基本数学知识的我们很清楚的知道输出并不是我们想要的结果(**精度丢失**),我们如何解决这个问题呢?一种很常用的方法是:**使用使用 BigDecimal 来定义浮点数的值,再进行浮点数的运算操作。**
具有基本数学知识的我们很清楚的知道输出并不是我们想要的结果(**精度丢失**),我们如何解决这个问题呢?一种很常用的方法是:**使用 BigDecimal 来定义浮点数的值,再进行浮点数的运算操作。**

```java
BigDecimal a = new BigDecimal("1.0");
Expand Down
18 changes: 9 additions & 9 deletions docs/java/multi-thread/万字详解ThreadLocal关键字.md
Original file line number Diff line number Diff line change
Expand Up @@ -186,7 +186,7 @@ public void set(T value) {
}

void createMap(Thread t, T firstValue) {
t.threadLocals = new `ThreadLocalMap`(this, firstValue);
t.threadLocals = new ThreadLocalMap(this, firstValue);
}
```

Expand Down Expand Up @@ -338,7 +338,7 @@ private void set(ThreadLocal<?> key, Object value) {
for (Entry e = tab[i];
e != null;
e = tab[i = nextIndex(i, len)]) {
`ThreadLocal`<?> k = e.get();
ThreadLocal<?> k = e.get();

if (k == key) {
e.value = value;
Expand Down Expand Up @@ -420,7 +420,7 @@ private void replaceStaleEntry(ThreadLocal<?> key, Object value,
(e = tab[i]) != null;
i = nextIndex(i, len)) {

`ThreadLocal`<?> k = e.get();
ThreadLocal<?> k = e.get();

if (k == key) {
e.value = value;
Expand Down Expand Up @@ -551,7 +551,7 @@ private int expungeStaleEntry(int staleSlot) {
for (i = nextIndex(staleSlot, len);
(e = tab[i]) != null;
i = nextIndex(i, len)) {
`ThreadLocal`<?> k = e.get();
ThreadLocal<?> k = e.get();
if (k == null) {
e.value = null;
tab[i] = null;
Expand Down Expand Up @@ -602,7 +602,7 @@ if (h != i) {

### `ThreadLocalMap`扩容机制

在``ThreadLocalMap.set()`方法的最后,如果执行完启发式清理工作后,未清理到任何数据,且当前散列数组中`Entry`的数量已经达到了列表的扩容阈值`(len*2/3)`,就开始执行`rehash()`逻辑:
在`ThreadLocalMap.set()`方法的最后,如果执行完启发式清理工作后,未清理到任何数据,且当前散列数组中`Entry`的数量已经达到了列表的扩容阈值`(len*2/3)`,就开始执行`rehash()`逻辑:

```java
if (!cleanSomeSlots(i, sz) && sz >= threshold)
Expand Down Expand Up @@ -653,7 +653,7 @@ private void resize() {
for (int j = 0; j < oldLen; ++j) {
Entry e = oldTab[j];
if (e != null) {
`ThreadLocal`<?> k = e.get();
ThreadLocal<?> k = e.get();
if (k == null) {
e.value = null;
} else {
Expand Down Expand Up @@ -711,7 +711,7 @@ private Entry getEntryAfterMiss(ThreadLocal<?> key, int i, Entry e) {
int len = tab.length;

while (e != null) {
`ThreadLocal`<?> k = e.get();
ThreadLocal<?> k = e.get();
if (k == key)
return e;
if (k == null)
Expand Down Expand Up @@ -773,7 +773,7 @@ public class InheritableThreadLocalDemo {
new Thread(new Runnable() {
@Override
public void run() {
System.out.println("子线程获取父类`ThreadLocal`数据:" + `ThreadLocal`.get());
System.out.println("子线程获取父类ThreadLocal数据:" + ThreadLocal.get());
System.out.println("子线程获取父类inheritableThreadLocal数据:" + inheritableThreadLocal.get());
}
}).start();
Expand All @@ -784,7 +784,7 @@ public class InheritableThreadLocalDemo {
打印结果:

```java
子线程获取父类`ThreadLocal`数据:null
子线程获取父类ThreadLocal数据:null
子线程获取父类inheritableThreadLocal数据:父类数据:inheritableThreadLocal
```

Expand Down