一、定义&优缺点
1. 2PC(两阶段提交)
- 原理:将事务提交过程分为准备阶段和提交阶段。协调者先询问所有参与者是否可以提交(准备阶段),若所有参与者都同意,则执行提交操作(提交阶段);若有任何一个参与者不同意,则执行回滚操作。
- 优点:实现相对简单,能够保证强一致性,在数据库事务等场景中应用广泛。
- 缺点:存在阻塞问题,即参与者在等待协调者的指令时可能会一直处于等待状态;协调者单点故障会导致整个系统不可用,影响系统的可靠性和可用性。
2. 3PC(三阶段提交)
- 原理:在2PC的基础上增加了一个预提交阶段。协调者先发送预提交请求,参与者收到后进入预提交状态;若所有参与者都准备好,则协调者再发送提交请求,参与者执行提交操作;若协调者未收到所有参与者的响应,则发送回滚请求。
- 优点:减少了阻塞问题,因为参与者在预提交阶段有一定的时间限制,不会一直等待协调者的指令;在一定程度上提高了系统的可用性。
- 缺点:仍然存在数据不一致的风险,例如在协调者发送提交请求后,部分参与者可能因网络问题未收到请求而未提交;实现复杂度较高,需要处理更多的状态和消息。
3. TCC(Try - Confirm - Cancel)
- 原理:将一个业务操作分为Try、Confirm和Cancel三个阶段。Try阶段主要是预留业务资源,检查业务规则是否满足;Confirm阶段是在Try阶段成功的基础上,确认执行业务操作;Cancel阶段是在Try阶段失败或需要回滚时,取消预留的资源。
- 优点:适用于业务逻辑复杂的场景,具有较高的灵活性,可以根据业务需求自定义Try、Confirm和Cancel的具体操作;能够实现最终一致性,在分布式系统中应用广泛。
- 缺点:需要业务代码实现Try、Confirm和Cancel逻辑,实现复杂度较高;对业务系统的侵入性较大,需要修改原有的业务代码。
二、流程图对比
1. 2PC流程图
2. 3PC流程图
3. TCC流程图
三、表格对比
特性 | 2PC | 3PC | TCC |
---|
阶段数 | 2 | 3 | 3 |
阻塞问题 | 存在 | 减少 | 不存在 |
数据一致性 | 强一致 | 弱一致 | 最终一致 |
实现复杂度 | 低 | 中 | 高 |
适用场景 | 数据库事务 | 对阻塞敏感的场景 | 业务逻辑复杂的场景 |
对业务侵入性 | 低 | 低 | 高 |
可靠性 | 受协调者单点故障影响 | 有一定提高但仍存在数据不一致风险 | 较高,通过最终一致性保证 |
四、Java代码对比
1. 2PC示例代码
import java.util.List;
interface Participant {
boolean prepare();
void commit();
void rollback();
}
class TwoPhaseCommitCoordinator {
public void execute(List<Participant> participants) {
boolean allPrepared = participants.stream().allMatch(Participant::prepare);
if (allPrepared) {
participants.forEach(Participant::commit);
} else {
participants.forEach(Participant::rollback);
}
}
}
class ExampleParticipant implements Participant {
@Override
public boolean prepare() {
System.out.println("Participant is preparing...");
return true;
}
@Override
public void commit() {
System.out.println("Participant is committing...");
}
@Override
public void rollback() {
System.out.println("Participant is rolling back...");
}
}
public class TwoPhaseCommitTest {
public static void main(String[] args) {
Participant participant1 = new ExampleParticipant();
Participant participant2 = new ExampleParticipant();
List<Participant> participants = List.of(participant1, participant2);
TwoPhaseCommitCoordinator coordinator = new TwoPhaseCommitCoordinator();
coordinator.execute(participants);
}
}
2. 3PC示例代码
import java.util.List;
interface Participant {
boolean prePrepare();
boolean prepare();
void commit();
void rollback();
}
class ThreePhaseCommitCoordinator {
public void execute(List<Participant> participants) {
boolean allPrePrepared = participants.stream().allMatch(Participant::prePrepare);
if (allPrePrepared) {
boolean allPrepared = participants.stream().allMatch(Participant::prepare);
if (allPrepared) {
participants.forEach(Participant::commit);
} else {
participants.forEach(Participant::rollback);
}
} else {
participants.forEach(Participant::rollback);
}
}
}
class ExampleParticipant3PC implements Participant {
@Override
public boolean prePrepare() {
System.out.println("Participant is pre - preparing...");
return true;
}
@Override
public boolean prepare() {
System.out.println("Participant is preparing...");
return true;
}
@Override
public void commit() {
System.out.println("Participant is committing...");
}
@Override
public void rollback() {
System.out.println("Participant is rolling back...");
}
}
public class ThreePhaseCommitTest {
public static void main(String[] args) {
Participant participant1 = new ExampleParticipant3PC();
Participant participant2 = new ExampleParticipant3PC();
List<Participant> participants = List.of(participant1, participant2);
ThreePhaseCommitCoordinator coordinator = new ThreePhaseCommitCoordinator();
coordinator.execute(participants);
}
}
3. TCC示例代码
import java.util.List;
interface Participant {
boolean tryOperation();
void confirm();
void cancel();
}
class TccCoordinator {
public void execute(List<Participant> participants) {
boolean allTried = participants.stream().allMatch(Participant::tryOperation);
if (allTried) {
participants.forEach(Participant::confirm);
} else {
participants.forEach(Participant::cancel);
}
}
}
class ExampleParticipantTCC implements Participant {
@Override
public boolean tryOperation() {
System.out.println("Participant is trying...");
return true;
}
@Override
public void confirm() {
System.out.println("Participant is confirming...");
}
@Override
public void cancel() {
System.out.println("Participant is canceling...");
}
}
public class TccTest {
public static void main(String[] args) {
Participant participant1 = new ExampleParticipantTCC();
Participant participant2 = new ExampleParticipantTCC();
List<Participant> participants = List.of(participant1, participant2);
TccCoordinator coordinator = new TccCoordinator();
coordinator.execute(participants);
}
}
五、关键维度总结
对比项 | 2PC | 3PC | TCC |
---|
性能 | 低(全局锁阻塞) | 中(缩短锁时间) | 高(仅Try阶段锁) |
一致性风险 | 协调者故障导致数据不一致 | 网络分区可能不一致 | 最终一致,需补偿 |
业务侵入性 | 低(依赖数据库) | 低 | 高(需实现Try/Confirm/Cancel) |
适用场景 | 传统金融跨库交易 | 分布式系统减少阻塞 | 高并发电商/支付 |
六、选型建议
- 强一致性要求高:选2PC(如银行转账)
- 可用性优先:选3PC或TCC(如秒杀系统)
- 复杂业务补偿:TCC+消息队列组合(如订单创建+库存扣减)
3PC 替代方案:可用 Seata AT 模式(改进版 2PC,通过 undo_log 实现无阻塞提交)