> For the complete documentation index, see [llms.txt](https://clu.gitbook.io/scrapbook/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://clu.gitbook.io/scrapbook/42-two-phase-commit.md).

# 4.2 Two-Phase-Commit

## Two-Phase-Commit

在分布式系統中, 每個節點雖然可以知道自己的操作是成功還是失敗, 卻無法知道其它的節點是成功還是失敗. 當一個transaction跨越了多個節點的時候, 為了保持transaction的ACID特性, 需要引入一個作為**協調者(Coordinator)**&#x7684;元件來統一掌握所有節點(或稱**參與者, Cohort**)的操作結果並最終指示這些節點是否要把操作結果真正的進行commit的動作.

再來, 看一下兩階段各有什麼事情要做:

* 第一階段
  1. 協調者會問所有參與者, 是否可以執行commit操作了
  2. 各個參與者開始transaction執行的準備工作, 如: lock resources, 預留resources, 寫undo/redo log...等
  3. 參與者回覆協調者, 如果transaction的準備工作成功, 則回應"可以commit", 否則回應"拒絕commit"
* 第二階段
  * 如果所有的參與者都回"可以commit", 那麼協調者就向所有的參與者發送"正式commit"的命令. 參與者完成正式commit, 並釋放所有資源, 然後回應"完成", 協調者收集各個節點的"完成"回應後結束這個global transaction
  * 如果有任何一個參與者回覆"拒絕commit", 那麼協調者向所有的參與者發送"rollback操作", 則參與者們釋放所有資源, 然後回應"rollback完成", 協調者收集各節點的"rollback"回應後, 取消這個global transaction

![](/files/-M523rF45UvmOEac6GoV)

所以你可以看到, 2PC其實就是種**第一階段投票**, **第二階段做決定**的演算法, 且基本上是**強一致性**的. 這個比Master-Slave的強一致性策略還要更保守一些(先嘗試再commit). 在一些系統設計中, 會串連一系列的呼叫, 譬如: A->B->C->D, 每一步都會分配一些資源或改寫一些資料. 舉個例子, 在B2C網站購物的下單操作在後端會有一系列的流程要跑. 如果一步一步的作, 就會出現這樣的問題, 如某一步做不下去了, 那麼前面每一次所分配的資源需要作反向操作把它們都回收掉, 所以操作起來比較複雜. 現在很多處理流程(workflow)都會借鑑2PC這個演算法, 使用try->confirm的流程來確保整個流程能夠成功完成.

另外, 在這段過程中我們也可以看到一些其它的問題:

* Synchronous blocking operation: 這很直觀, 畢竟從名稱一看就知道會大幅影響性能
* Timeout, 大概有以下幾種情境:
  1. 如果第一階段中, 參與者沒有收到詢問請求, 或是參與者的回應沒有送到協調者那邊. 那麼協調者要針對timeout做處理, 一但timeout, 可以當作失敗, 也可以retry.
  2. 如果第二階段中, 正式commit發出後, 若有的參與者沒有收到, 或是參與者commit/rollback後的確認訊息沒有返回, 一但參與者的回應timeout了, 要就retry, 不然就把那個參與者標記為問題節點踢出整個集群, 這樣可以保證服務節點都是資料一致性的.
  3. 糟糕的情形是, 第二階段中, 如果參與者收不到協調者的commit/rollback指令, 則參與者將會處於"狀態未知"的階段, 參與者完全不知道要怎麼辦, 比如: 若所有的參與者完成第一階段的回覆後 (可能全yes/全no/部分yes部分no), 但協調者這時候卻掛掉了, 那所有的參與者都會完全不知道該怎麼辦(不可以問別的參與者). 若出了這種問題, 為了一致性, 要就死等協調者, 不然就重發第一階段的yes/no命令.

對2PC來說, timeout的第三種情形應該就是最大的問題了, 如果第一階段完成後, 參與者在第二階段沒有收到決策, 那資料節點就會進入一種不知所措的狀態, 這個狀態會block住整個transaction. 即是說, 協調者對於transaction的完成非常重要, 協調者的可用性是關鍵點. 因此, 出現了三階段提交(Three-Phase-Commit, 3PC), 這邊直接copy維基百科的圖, 其把2PC的第一階段分成兩段: 先詢問, 然後再鎖資源. 最後才真的commit.

![](/files/-M523rF6P44hWIr7MBDD)

3PC的核心理念是: **在詢問的時候並不鎖定資源, 除非所有人都同意了, 才開始鎖資源.**

理論上來說, 若第一階段所有的節點回傳成功, 那麼有理由相信成功commit的機率很大. 如此可以降低參與者的狀態未知的機率. 也就是說, 一但參與者收到了precommit, 意味著它知道大家其實都同意修改了, 這是很重要的一點. 下面這張圖是3PC的狀態轉換圖(圖中的虛線F為Failuer, T為Timeout, Q是Query, a為Abort, w-Wait, p-PreCommit, c-Commit):

![](/files/-M523rF8xx5SdJRzDVGT)

從上圖的狀態變化圖可以從虛線看到, 如果結果處在P狀態(PreCommit)的時候發生了F/T的問題, 3PC比2PC好的地方是: 3PC可以直接繼續把狀態變成C(Commit), 但2PC會不知所措.

其實3PC是一個很複雜的東西, 實作起來除了難之外, 也會有一些問題.

看到這邊不要說你, 我自己也有很多的問題, 譬如說思考2PC/3PC中各種各樣的失敗場景, 其實你會發現timeout是個很難處理的東西, 因為網路上的timeout在很多時候讓你無所適從, 你也不知道對方是做了還是沒做. 然後好好的一個狀態機就因為timeout成了個擺設.

一個網路服務會有三種狀態:

* **Success**
* **Failure**
* **Timeout**

**我說第三個絕對是惡夢, 尤其在需要維護狀態的時候.**

這篇文章是從[這裡](http://coolshell.cn/articles/10910.html)看來的, 我還會再整理過


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://clu.gitbook.io/scrapbook/42-two-phase-commit.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
