08 November 2017

2pc

在 分布式系统 下,一个程序的运行的成功与否 => 依赖于多个结果,这些结果在多台计算机(两台或两台以上)。
只有当所有运行结果都成功的情况下才算成功,如果其中一个结果失败就算全部失败。

课本上用烂了的例子:银行转账,用户 A 给 用户 B 转账 100 元:

  1. A 用户的余额先减 100
  2. B 用户的余额加 100

引入事务和锁的概念:
事务指的一串操作的集合,上面的例子可以看成是两个事务
在事务的执行过程中,我们需要把一些东西锁 (lock) 起来。在上面的例子中,A 用户的余额就要加锁,防止被另外一个事务访问。不加锁有什么问题?
如果有两个程序同时要减用户 100 的余额,用户原本有 300 元

正常的逻辑是

result = 300 - 100 ==> 200 (程序 1)
result = 200 - 100 ==> 100 (程序 2)
==> 最后用户的余额是 100 元

然后,由于是同时访问,两个程序一开始进来拿到的用户余额都是 300 元,就会出现下面的问题

result = 300 - 100 ==> 200 (程序 1)
result = 300 - 100 ==> 200 (程序 2)
==> 最后用户的余额还是 200 元

在加锁的情况下,就不会出现上面的问题,程序 2 必须要等到程序 1 操作完解锁才能拿到用户的余额。可以简单的理解,事务就是对一些 “资源” 加了锁的操作

回到银行转账的例子,上面的两个事务如果其中一个失败,两个操作都不应该被执行。
操作 2 需要知道操作 1 的结果,这时候需要引入一个 “协调者”:

  1. 事务 1 将自己的结果告诉 “协调者”,成功了
  2. 事务 2 将自己的结果告诉 “协调者”,成功了
  3. 协调者知道上面的事务都成功了,真正的执行 (commit)

另一种情况

  1. 事务 1 将自己的结果告诉 “协调者”,失败了
  2. 事务 2 将自己的结果告诉 “协调者”,xxx (成功/失败)
  3. 协调者知道上面发生了一个失败,执行回滚 (rollback 什么都不做)

二阶段提交的意思就是把上面的步骤分成两个

  1. 各个事务将自己的运行结果告诉 “协调者”
  2. 协调者 根据收集到的结果决定上面的操作是否都可以执行 (rollback/commit)

2-1

2-2

2pc 存在的问题

  1. 同步阻塞
    各个参与者都在等待其他参与者响应的过程中,将无法进行其他任何事务
  2. 单点问题
    协调者容易出现单点故障。
    如果协调者在阶段 2 出现问题,那么其他参与者都处于 锁定事务资源的状态中,无法继续完成事务事务
  3. 数据不一致
    阶段 2 协调者向所有参与者发送 commit 的时候,如果只有一部分参与者收到了,其他参与者没收到 commit 请求。
    那么会导致只有一部分数据 commit 了,另外一部分没有 commit,导致数据不一致
  4. 太过保守
    没有完善的容错机制,任何一个节点的失败都会导致整个事务的失败

3pc

3pc 和 2pc 的区别

3pc 把 2pc 的第一个阶段拆成两个阶段
2pc 的第一个阶段是当参与者收到 协调者 是否 ready 好这个请求的时候,如果参与者准备好了,就 lock 资源,执行事务,再发送执行结果给协调者,然后进入第二个阶段。
3pc 的第一个阶段是先询问当参与者是否 ready 好。然后在第二个阶段再 lock 资源,执行事务,再发送执行结果给协调者,然后进入第三个阶段。

另外一个区别就是在第二个阶段的时候,参与者收到了 preCommit 请求之后,参与者可以不需要协调者,当参与者等待协调者在第三阶段的 commit/rollback 的响应这个期间发生 timeout 的时候,参与者默认认为协调者发的 commit 响应

另外,3pc 和 2pc 的区别就是加入了 timeout(轻微的防止同步阻塞的情况)

3-1

3-2

3-3

相关阅读