auto commit
This commit is contained in:
parent
b486e645a2
commit
d90eb6fc00
42
notes/分布式.md
42
notes/分布式.md
|
@ -173,7 +173,7 @@ Zookeeper 提供了一种树形结构级的命名空间,/app1/p_1 节点的父
|
||||||
|
|
||||||
网络分区指分布式系统中的节点被划分为多个区域,每个区域内部可以通信,但是区域之间无法通信。
|
网络分区指分布式系统中的节点被划分为多个区域,每个区域内部可以通信,但是区域之间无法通信。
|
||||||
|
|
||||||
在分区容忍性条件下,分布式系统在遇到任何网络分区故障的时候,仍然需要能对外提供服务,除非是整个网络环境都发生了故障。
|
在分区容忍性条件下,分布式系统在遇到任何网络分区故障的时候,仍然需要能对外提供一致性和可用性的服务,除非是整个网络环境都发生了故障。
|
||||||
|
|
||||||
## 权衡
|
## 权衡
|
||||||
|
|
||||||
|
@ -181,8 +181,8 @@ Zookeeper 提供了一种树形结构级的命名空间,/app1/p_1 节点的父
|
||||||
|
|
||||||
可用性和一致性往往是冲突的,很难使它们同时满足。在多个节点之间进行数据同步时,
|
可用性和一致性往往是冲突的,很难使它们同时满足。在多个节点之间进行数据同步时,
|
||||||
|
|
||||||
- 为了保证一致性(CP),需要牺牲部分节点的可用性(即部分节点虽仍正常工作但不会响应用户请求),但保证读出的数据一定是具有强一致性的;
|
- 为了保证一致性(CP),不能访问未同步完成的节点,也就失去了部分可用性;
|
||||||
- 为了保证可用性(AP),在同步过程中允许读取所有节点的数据,但是数据可能不一致。
|
- 为了保证可用性(AP),允许读取所有节点的数据,但是数据可能不一致。
|
||||||
|
|
||||||
<div align="center"> <img src="../pics//0b587744-c0a8-46f2-8d72-e8f070d67b4b.jpg"/> </div><br>
|
<div align="center"> <img src="../pics//0b587744-c0a8-46f2-8d72-e8f070d67b4b.jpg"/> </div><br>
|
||||||
|
|
||||||
|
@ -226,17 +226,41 @@ ACID 要求强一致性,通常运用在传统的数据库系统上。而 BASE
|
||||||
|
|
||||||
## 执行过程
|
## 执行过程
|
||||||
|
|
||||||
规定一个提议包含一个提议序号 n,其中序号具有唯一性。
|
规定一个提议包含两个字段:[n, v],其中 n 为序号(具有唯一性),v 为提议值。
|
||||||
|
|
||||||
当 Acceptor 接收到一个Prepare请求,包含的提议序号为 n1 ,并且之前还未接收过Prepare请求,那么发送一个响应,设置当前接收到的提议为 n1 ,并且保证以后不会再接受序号小于 n1 的提议。
|
### 1. Prepare 阶段
|
||||||
|
|
||||||
如果 Acceptor 接收到一个Prepare请求,其包含的提议号为 n2,如果 n2 大于其之前响应的所有的 Prepare 消息所附带的提议号,并且大于其之前响应的所有 Accept 消息所附带的提议号,则发送响应消息,并在响应消息中附带上其已经 Accept 过的提议,并保证不再接受序号小于 n2 的消息。
|
下图演示了两个 Proposer 和三个 Acceptor 的系统中运行该算法的初始过程,每个 Proposer 都会向所有 Acceptor 发送 Prepare 请求。
|
||||||
|
|
||||||
当一个 Proposer 接收到超过一半 Acceptor 的 Prepare 响应时,就可以发送 Accept 请求。Accept 请求包括 Prepare 阶段使用的提议号以及提议值,如果 Prepare阶段没有 Acceptor 回应任何已经 Accept 的值则该 Proposer 可以自己选择一个,否则使用 Prepare 阶段中所有收到的值中提议序号最高的一个。
|
<div align="center"> <img src="../pics//1a9977e4-2f5c-49a6-aec9-f3027c9f46a7.png"/> </div><br>
|
||||||
|
|
||||||
Acceptor 收到 Accept 请求后,如果该请求中的提议序号大于等于其已经 Accept 或者在 Prepare 阶段收到的提议的序号,则接受该值。否则丢弃该消息。
|
当 Acceptor 接收到一个 Prepare 请求,包含的提议为 [n1, v1],并且之前还未接收过 Prepare 请求,那么发送一个 Prepare 响应,设置当前接收到的提议为 [n1, v1],并且保证以后不会再接受序号小于 n1 的提议。
|
||||||
|
|
||||||
Acceptor 接收到 Accept 请求时,如果序号大于等于该 Acceptor 承诺的最小序号,那么就发送通知给所有的 Learner。当 Learner 发现有大多数的 Acceptor 接收了某个提议,那么该提议的提议值就被 Paxos 选择出来。
|
如下图,Acceptor X 在收到 [n=2, v=8] 的 Prepare 请求时,由于之前没有接收过提议,因此就发送一个 [no previous] 的 Prepare 响应,设置当前接收到的提议为 [n=2, v=8],并且保证以后不会再接受序号小于 2 的提议。其它的 Acceptor 类似。
|
||||||
|
|
||||||
|
<div align="center"> <img src="../pics//fb44307f-8e98-4ff7-a918-31dacfa564b4.jpg"/> </div><br>
|
||||||
|
|
||||||
|
如果 Acceptor 接收到一个 Prepare 请求,包含的提议为 [n2, v2],并且之前已经接收过提议 [n1, v1]。如果 n1 > n2,那么就丢弃该提议请求;否则,发送 Prepare 响应,该 Prepare 响应包含之前已经接收过的提议 [n1, v1],设置当前接收到的提议为 [n2, v2],并且保证以后不会再接受序号小于 n2 的提议。
|
||||||
|
|
||||||
|
如下图,Acceptor Z 收到 Proposer A 发来的 [n=2, v=8] 的 Prepare 请求,由于之前已经接收过 [n=4, v=5] 的提议,并且 n > 2,因此就抛弃该提议请求;Acceptor X 收到 Proposer B 发来的 [n=4, v=5] 的 Prepare 请求,因为之前接收到的提议为 [n=2, v=8],并且 2 <= 4,因此就发送 [n=2, v=8] 的 Prepare 响应,设置当前接收到的提议为 [n=4, v=5],并且保证以后不会再接受序号小于 4 的提议。Acceptor Y 类似。
|
||||||
|
|
||||||
|
<div align="center"> <img src="../pics//2bcc58ad-bf7f-485c-89b5-e7cafc211ce2.jpg"/> </div><br>
|
||||||
|
|
||||||
|
### 2. Accept 阶段
|
||||||
|
|
||||||
|
当一个 Proposer 接收到超过一半 Acceptor 的 Prepare 响应时,就可以发送 Accept 请求。
|
||||||
|
|
||||||
|
Proposer A 接收到两个 Prepare 响应之后,就发送 [n=2, v=8] Accept 请求。该 Accept 请求会被所有 Acceptor 丢弃,因为此时所有 Acceptor 都保证不接受序号小于 4 的提议。
|
||||||
|
|
||||||
|
Proposer B 过后也收到了两个 Prepare 响应,因此也开始发送 Accept 请求。需要注意的是,Accept 请求的 v 需要取它收到的最大 v 值,也就是 8。因此它发送 [n=4, v=8] 的 Accept 请求。
|
||||||
|
|
||||||
|
<div align="center"> <img src="../pics//9b838aee-0996-44a5-9b0f-3d1e3e2f5100.png"/> </div><br>
|
||||||
|
|
||||||
|
### 3. Learn 阶段
|
||||||
|
|
||||||
|
Acceptor 接收到 Accept 请求时,如果序号大于等于该 Acceptor 承诺的最小序号,那么就发送 Learn 提议给所有的 Learner。当 Learner 发现有大多数的 Acceptor 接收了某个提议,那么该提议的提议值就被 Paxos 选择出来。
|
||||||
|
|
||||||
|
<div align="center"> <img src="../pics//bf667594-bb4b-4634-bf9b-0596a45415ba.jpg"/> </div><br>
|
||||||
|
|
||||||
## 约束条件
|
## 约束条件
|
||||||
|
|
||||||
|
|
|
@ -625,7 +625,7 @@ void take_two(int i) {
|
||||||
down(&s[i]);
|
down(&s[i]);
|
||||||
}
|
}
|
||||||
|
|
||||||
void put_two(i) {
|
void put_tow(i) {
|
||||||
down(&mutex);
|
down(&mutex);
|
||||||
state[i] = THINKING;
|
state[i] = THINKING;
|
||||||
test(LEFT);
|
test(LEFT);
|
||||||
|
|
|
@ -149,7 +149,7 @@ uniqueInstance 采用 volatile 关键字修饰也是很有必要的, `uniqueIn
|
||||||
2. 初始化 uniqueInstance
|
2. 初始化 uniqueInstance
|
||||||
3. 将 uniqueInstance 指向分配的内存地址
|
3. 将 uniqueInstance 指向分配的内存地址
|
||||||
|
|
||||||
但是由于 JVM 具有指令重排的特性,执行顺序有可能变成 1>3>2。指令重排在单线程环境下不会出现问题,但是在多线程环境下会导致一个线程获得还没有初始化的实例。例如,线程 T<sub>1</sub> 执行了 1 和 3,此时 T<sub>2</sub> 调用 getUniqueInstance() 后发现 uniqueInstance 不为空,因此返回 uniqueInstance,但此时 uniqueInstance 还未被初始化。
|
但是由于 JVM 具有指令重排的特性,执行顺序有可能变成 1>3>2。指令重排在单线程环境下不会出先问题,但是在多线程环境下会导致一个线程获得还没有初始化的实例。例如,线程 T<sub>1</sub> 执行了 1 和 3,此时 T<sub>2</sub> 调用 getUniqueInstance() 后发现 uniqueInstance 不为空,因此返回 uniqueInstance,但此时 uniqueInstance 还未被初始化。
|
||||||
|
|
||||||
使用 volatile 可以禁止 JVM 的指令重排,保证在多线程环境下也能正常运行。
|
使用 volatile 可以禁止 JVM 的指令重排,保证在多线程环境下也能正常运行。
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue
Block a user