兩階段提交協(xié)議(Two-phase Commit忍啤,2PC)經(jīng)常被用來(lái)實(shí)現(xiàn)分布式事務(wù)。一般分為協(xié)調(diào)器C和若干事務(wù)執(zhí)行者Si兩種角色,這里的事務(wù)執(zhí)行者就是具體的數(shù)據(jù)庫(kù)沦童,協(xié)調(diào)器可以和事務(wù)執(zhí)行器在一臺(tái)機(jī)器上。
- 我們的應(yīng)用程序(client)發(fā)起一個(gè)開始請(qǐng)求到TC柜候;
- TC先將<prepare>消息寫到本地日志搞动,之后向所有的Si發(fā)起<prepare>消息。以支付寶轉(zhuǎn)賬到余額寶為例渣刷,TC給A的prepare消息是通知支付寶數(shù)據(jù)庫(kù)相應(yīng)賬目扣款1萬(wàn)鹦肿,TC給B的prepare消息是通知余額寶數(shù)據(jù)庫(kù)相應(yīng)賬目增加1w。為什么在執(zhí)行任務(wù)前需要先寫本地日志辅柴,主要是為了故障后恢復(fù)用箩溃,本地日志起到現(xiàn)實(shí)生活中憑證 的效果瞭吃,如果沒有本地日志(憑證),出問題容易死無(wú)對(duì)證涣旨;
- Si收到<prepare>消息后歪架,執(zhí)行具體本機(jī)事務(wù),但不會(huì)進(jìn)行commit霹陡,如果成功返回<yes>和蚪,不成功返回<no>。同理烹棉,返回前都應(yīng)把要返回的消息寫到日志里攒霹,當(dāng)作憑證。
- TC收集所有執(zhí)行器返回的消息浆洗,如果所有執(zhí)行器都返回yes催束,那么給所有執(zhí)行器發(fā)生送commit消息,執(zhí)行器收到commit后執(zhí)行本地事務(wù)的commit操作伏社;如果有任一個(gè)執(zhí)行器返回no抠刺,那么給所有執(zhí)行器發(fā)送abort消息,執(zhí)行器收到abort消息后執(zhí)行事務(wù)abort操作摘昌。
注:TC或Si把發(fā)送或接收到的消息先寫到日志里速妖,主要是為了故障后恢復(fù)用。如某一Si從故障中恢復(fù)后第焰,先檢查本機(jī)的日志买优,如果已收到<commit >,則提交挺举,如果<abort >則回滾杀赢。如果是<yes>,則再向TC詢問一下湘纵,確定下一步脂崔。如果什么都沒有,則很可能在<prepare>階段Si就崩潰了梧喷,因此需要回滾砌左。
現(xiàn)如今實(shí)現(xiàn)基于兩階段提交的分布式事務(wù)也沒那么困難了,如果使用Java铺敌,那么可以使用開源軟件atomikos(http://www.atomikos.com/)來(lái)快速實(shí)現(xiàn)汇歹。
不過但凡使用過的上述兩階段提交的同學(xué)都可以發(fā)現(xiàn)性能實(shí)在是太差,根本不適合高并發(fā)的系統(tǒng)偿凭。為什么产弹?
- 兩階段提交涉及多次節(jié)點(diǎn)間的網(wǎng)絡(luò)通信,通信時(shí)間太長(zhǎng)弯囊!
- 事務(wù)時(shí)間相對(duì)于變長(zhǎng)了痰哨,鎖定的資源的時(shí)間也變長(zhǎng)了胶果,造成資源等待時(shí)間也增加好多!
正是由于分布式事務(wù)存在很嚴(yán)重的性能問題斤斧,大部分高并發(fā)服務(wù)都在避免使用早抠,往往通過其他途徑來(lái)解決數(shù)據(jù)一致性問題。