1
0
mirror of https://github.com/chai2010/advanced-go-programming-book.git synced 2025-05-24 04:22:22 +00:00

add img for ch6-3

This commit is contained in:
Xargin 2018-12-24 00:06:05 +08:00
parent 903b12bf20
commit 243aa31f1a
5 changed files with 4 additions and 106 deletions

View File

@ -19,38 +19,7 @@ timer 的实现在工业界已经是有解的问题了。常见的就是时间
最常见的时间堆一般用小顶堆实现,小顶堆其实就是一种特殊的二叉树:
```
┌─────┐
│ │
│ 5 │
└─────┘
┌──────────┴──────────┐
│ │
▼ ▼
┌─────┐ ┌─────┐
│ │ │ │
│ 6 │ │ 10 │
└─────┘ └─────┘
│ │
┌────┴─────┐ ┌────┴─────┐
│ │ │ │
▼ ▼ ▼ ▼
┌─────┐ ┌─────┐ ┌─────┐ ┌─────┐
│ │ │ │ │ │ │ │
│ 7 │ │ 6 │ │ 11 │ │ 20 │
└─────┘ └─────┘ └─────┘ └─────┘
│ │
│ │
┌───────┴────┐ └───────┐
│ │ │
▼ ▼ ▼
┌─────┐ ┌─────┐ ┌─────┐
│ │ │ │ ............... │ │
│ 15 │ │ 8 │ │ 30 │
└─────┘ └─────┘ └─────┘
```
![二叉堆](../images/ch6-binary_tree.png)
小顶堆的好处是什么呢?实际上对于定时器来说,如果堆顶元素比当前的时间还要大,那么说明堆内所有元素都比当前时间大。进而说明这个时刻我们还没有必要对时间堆进行任何处理。所以对于定时 check 来说,时间复杂度是 O(1) 的。
@ -58,34 +27,7 @@ timer 的实现在工业界已经是有解的问题了。常见的就是时间
Go 自身的 timer 就是用时间堆来实现的,不过并没有使用二叉堆,而是使用了扁平一些的四叉堆。在最近的版本中,还加了一些优化,我们先不说优化,先来看看四叉的小顶堆长什么样:
```
+-----+
| |
| 0 |
+-----+
|
|
|
v
+-----+-----+-----+-----+
| | | | |
| 3 | 2 | 2 | 10 |
+-----+-----+-----+-----+
| | | |
| | | |
+----------+ | | | |
+----------------+ 4*i+1 +-----------------------+ | | +-----------------------------+
| +----------+ +-------------------+ +---+ |
| | | |
| | | |
v | | v
+-----+-----+-----+-----+ | | +-----+-----+-----+-----+
| | | | | v v | | | | |
| 20 | 4 | 5 | 13 | +-----+-----+-----+-----+ +-----+-----+-----+-----+ | 99 | 13 | 11 | 12 |
+-----+-----+-----+-----+ | | | | | | | | | | +-----+-----+-----+-----+
| 12 | 14 | 15 | 16 | | 3 | 10 | 3 | 3 |
+-----+-----+-----+-----+ +-----+-----+-----+-----+
```
![四叉堆](../images/ch6-four-branch-tree.png)
小顶堆的性质,父节点比其 4 个子节点都小,子节点之间没有特别的大小关系要求。
@ -126,29 +68,7 @@ Go 自身的 timer 就是用时间堆来实现的,不过并没有使用二叉
我们可以参考 elasticsearch 的设计,每份任务数据都有多个副本,这里假设两副本:
```
┌──────────┐
│ node 1 │
├──────────┴────────────────────────┐
│ ┏━━━┓ ┌───┐ ┌───┐ ┏━━━┓ │
│ ┃ 0 ┃ │ 1 │ │ 3 │ ┃ 4 ┃ │
│ ┗━━━┛ └───┘ └───┘ ┗━━━┛ │
└───────────────────────────────────┘
┌──────────┐
│ node 2 │
├──────────┴────────────────────────┐
│ ┌───┐ ┏━━━┓ ┏━━━┓ │
│ │ 0 │ ┃ 2 ┃ ┃ 3 ┃ │
│ └───┘ ┗━━━┛ ┗━━━┛ │
└───────────────────────────────────┘
┌──────────┐
│ node 3 │
├──────────┴────────────────────────┐
│ ┏━━━┓ ┌───┐ ┌───┐ │
│ ┃ 1 ┃ │ 2 │ │ 4 │ │
│ ┗━━━┛ └───┘ └───┘ │
└───────────────────────────────────┘
```
![数据分布](../images/ch6-data-dist1.png)
一份数据虽然有两个持有者,但持有者持有的副本会进行区分,比如持有的是主副本还是非主副本,主副本在图中为摸黑部分,非主副本为正常线条。
@ -156,29 +76,7 @@ Go 自身的 timer 就是用时间堆来实现的,不过并没有使用二叉
当有机器故障时,任务数据需要进行 rebalance 工作,比如 node 1 挂了:
```
┌──────────┐
│ node 1 │
├──────────┴────────────────────────┐
│ │
│ X X X │
│ │
└───────────────────────────────────┘
┌──────────┐
│ node 2 │
├──────────┴────────────────────────┐
│ ┌───┐ ┌───┐ ┏━━━┓ ┏━━━┓ ┏━━━┓ │
│ │ 0 │ │ 1 │ ┃ 2 ┃ ┃ 3 ┃ ┃ 4 ┃ │
│ └───┘ └───┘ ┗━━━┛ ┗━━━┛ ┗━━━┛ │
└───────────────────────────────────┘
┌──────────┐
│ node 3 │
├──────────┴────────────────────────┐
│ ┏━━━┓ ┏━━━┓ ┌───┐ ┌───┐ ┌───┐ │
│ ┃ 0 ┃ ┃ 1 ┃ │ 2 │ │ 3 │ │ 4 │ │
│ ┗━━━┛ ┗━━━┛ └───┘ └───┘ └───┘ │
└───────────────────────────────────┘
```
![数据分布2](../images/ch6-data-dist2.png)
node 1 的数据会被迁移到 node 2 和 node 3 上。

BIN
images/ch6-binary_tree.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

BIN
images/ch6-data-dist1.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

BIN
images/ch6-data-dist2.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB