本文目录导读:

在 PHP 项目中处理分布式事务,主要有以下几种主流方案,我会从实现难度、一致性强度、适用场景三个维度来分析。
核心痛点
在微服务或分布式架构下,一个业务操作往往跨越多个服务、多个数据库,传统数据库的 ACID 事务无法跨服务生效,因此需要引入分布式事务机制。
主流方案对比
| 方案 | 一致性 | 性能 | 侵入性 | 适用场景 |
|---|---|---|---|---|
| 2PC(两阶段提交) | 强一致 | 低 | 高 | 银行、金融核心系统 |
| TCC(Try-Confirm-Cancel) | 最终一致(业务层控制) | 中 | 高 | 扣款+发货等强约束场景 |
| 本地消息表 | 最终一致 | 高 | 中 | 异步解耦、非实时性要求 |
| MQ 事务消息(RocketMQ) | 最终一致 | 高 | 中 | 异步解耦、通知类 |
| Saga(长事务) | 最终一致 | 高 | 中 | 聚合根之间的长链路业务 |
主流方案详解(含 PHP 代码实现)
方案 1:2PC(两阶段提交)
原理:协调者向所有参与者发送准备请求,全部 ACK 后发送提交指令。
PHP 实现示例:
class TwoPhaseCommit
{
private array $participants = [];
private array $preparedResources = [];
public function addParticipant(TransactionParticipant $participant): void
{
$this->participants[] = $participant;
}
public function execute(): bool
{
// Phase 1: Prepare
foreach ($this->participants as $participant) {
$result = $participant->prepare();
if (!$result) {
$this->rollback();
return false;
}
$this->preparedResources[] = $participant;
}
// Phase 2: Commit
foreach ($this->preparedResources as $participant) {
$participant->commit();
}
return true;
}
private function rollback(): void
{
foreach ($this->preparedResources as $participant) {
$participant->rollback();
}
}
}
⚠️ 缺点:阻塞式、性能差、协调者单点问题。
方案 2:TCC(Try-Confirm-Cancel)★ 推荐
原理:业务层实现 Try(预留资源)、Confirm(确认执行)、Cancel(回滚补偿)。
PHP 实现示例:
interface TccActionInterface
{
public function try(): bool;
public function confirm(): bool;
public function cancel(): bool;
}
class OrderTccService
{
private Redis $redis; // 存储事务执行状态
public function createOrder(Order $order): void
{
$txId = uniqid('tx_', true);
$status = new TransactionStatus($txId);
// Step 1: Try 阶段 - 各参与者预留资源
$reserveResult = $this->reserveInventory($order); // 扣减库存(冻结)
$deductResult = $this->deductBalance($order); // 扣减用户余额(冻结)
if ($reserveResult && $deductResult) {
// Step 2: Confirm 阶段
$this->confirmOrder($txId);
} else {
// Step 3: Cancel 阶段 - 回滚
$this->cancel($txId);
}
}
private function confirmOrder(string $txId): void
{
// 真实扣减,释放预占库存
// 执行下单确认逻辑
}
private function cancel(string $txId): void
{
// 释放库存预占
// 返还用户冻结金额
}
}
✅ 特点:
- 业务侵入性强(需要写
try/confirm/cancel三套逻辑) - 无锁、无阻塞、性能较好
- 必须处理好幂等性和空回滚问题
方案 3:本地消息表 ★ 最常用
原理:在事务性数据库中存消息记录,通过定时任务或 MQ 异步投递。
PHP 实现示例(Laravel + MySQL):
class OrderService
{
public function createOrder(array $orderData): void
{
// 开启本地事务
DB::transaction(function () use ($orderData) {
// 1. 创建订单
$order = Order::create($orderData);
// 2. 写入本地消息表(同库同事务)
Message::create([
'msg_key' => 'order_created',
'payload' => json_encode($order),
'status' => 'pending',
'retry_count' => 0,
]);
});
// 异步发送消息
$this->publishMessageToMQ();
}
private function publishMessageToMQ(): void
{
$pendingMessages = Message::where('status', 'pending')
->limit(100)
->get();
foreach ($pendingMessages as $message) {
// 发送到 RabbitMQ / Kafka
if (MQ::publish($message->msg_key, $message->payload)) {
$message->update(['status' => 'sent']);
}
}
}
}
✅ 优点:
- 实现简单,不依赖特定中间件
- 保证最终一致性
- 注意:需要维护消息表,定时扫描重发
方案 4:MQ 事务消息(RocketMQ)★ 高性能推荐
原理:利用 RocketMQ 的半事务消息机制,本地事务执行后提交,MQ 自动重试投递。
<?php
use RocketMQ\Producer;
use RocketMQ\TransactionListener;
use RocketMQ\Message;
class OrderTransactionListener implements TransactionListener
{
public function executeLocalTransaction(Message $msg): string
{
$orderData = json_decode($msg->getBody(), true);
// 本地事务
try {
DB::transaction(function () use ($orderData) {
Order::create($orderData);
});
return LocalTransactionState::COMMIT;
} catch (\Exception $e) {
return LocalTransactionState::ROLLBACK;
}
}
public function checkLocalTransaction(Message $msg): string
{
$orderId = $msg->getProperty('order_id');
$order = Order::find($orderId);
return $order ? LocalTransactionState::COMMIT : LocalTransactionState::ROLLBACK;
}
}
// 发送事务消息
$producer = new Producer();
$producer->start();
$msg = new Message('order-topic', json_encode($orderData));
$msg->setProperty('order_id', $order->id);
$result = $producer->sendMessageInTransaction($msg, new OrderTransactionListener());
✅ 特点:
- 无本地消息表,简洁
- 需要部署 RocketMQ(或阿里云 MQ)
- 适合消息可靠性要求高的场景
推荐架构组合策略
在实际 PHP 项目中,建议根据业务场景组合使用:
┌─────────────────────────────────────────────────────┐
│ 业务场景 │
├─────────────────┬───────────────────────────────────┤
│ 强一致性核心链路 │ 2PC / TCC │
│ 用户余额/库存 │ │
│ │ │
│ 最终一致性场景 │ 本地消息表 / MQ 事务消息 │
│ 订单通知/积分 │ │
│ │ │
│ 长链路聚合根 │ Saga(编排式/协同式) │
│ 跨 N 个服务 │ │
└─────────────────┴───────────────────────────────────┘
实战中的关键注意事项
幂等性设计(重中之重)
class IdempotencyUtil
{
public static function isProcessed(string $key): bool
{
$redis = app('redis');
return (bool) $redis->setnx("idempotent:{$key}", 1);
}
public static function release(string $key): void
{
app('redis')->del("idempotent:{$key}");
}
}
补偿机制
- 定时扫描未完成的事务记录
- 超时自动触发 Cancel 或补偿重试
- 记录完整的事务日志便于人工干预
监控与告警
// 记录分布式事务状态到 ELK / Prometheus
Metrics::increment('distributed_tx_total');
Metrics::increment('distributed_tx_failed');
总结建议
| 项目阶段 | 推荐方案 | 原因 |
|---|---|---|
| 早期单体 + 分库 | 本地消息表 | 简单可靠,无运维成本 |
| 中期微服务化 | TCC + 本地消息表 | 兼顾强一致和最终一致 |
| 大规模高并发 | RocketMQ 事务消息 + Saga | 性能最优,运维成熟 |
| 要求强一致 | 2PC / 分布式数据库(如 TiDB) | 数据一致性要求高于性能 |
最终原则:
- 非必要不分布式事务,尽量通过事件溯源 + 最终一致性解决。
- 将业务边界划分清晰,减少跨服务事务。
- 每个服务必须保障自己数据的完整性,通过补偿而非全局锁。
根据你的项目规模、团队运维能力和业务一致性要求,选择合适的方案组合即可,如果只是小规模项目,先推荐本地消息表方案,侵入性低且实现成本最低。
版权声明:NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播