文档目录
事务管理

简介

本文件聚焦 DouPHP 后台服务层的事务管理能力,系统梳理数据库事务的生命周期(开启、提交、回滚)与最佳实践,覆盖复杂业务场景下的事务模式(嵌套事务、分布式事务、长事务优化),并提供在业务服务类中正确使用事务的示例指引、异常处理、隔离级别设置、性能监控、缓存一致性保证以及失败恢复策略。同时给出常见事务问题的排查方法与优化建议,帮助开发者在生产环境中稳定、高效地使用事务。

项目结构

DouPHP 通过静态门面 DB 暴露统一的数据库访问能力,底层由 Connection 容器单例承载连接与事务状态;业务侧在控制器或服务中直接调用 DB::beginTransaction()/commit()/rollback() 完成事务控制。订单定时任务、后台订单批量操作、用户微信登录等关键路径均使用事务保障数据一致性。

graph TB
A["业务服务/控制器"] --> B["DB 静态门面<br/>core/facade/DB.php"]
B --> C["Connection 容器单例<br/>core/infra/database/Connection.php"]
C --> D["数据库连接池/适配器<br/>plugin/*/sdk/.../DbConnectionManager.php"]
D --> E["具体数据库驱动"]

核心组件

  • DB 静态门面:对外提供 table、where、data、insert/update/delete、lock、beginTransaction/commit/rollback 等统一入口,内部委托给 Connection 实例。
  • Connection:维护当前连接的查询构造器与事务状态,负责执行 SQL 与事务边界控制。
  • DbHandle/DbConnectionManager:插件 SDK 中的数据库连接管理与事务封装,体现事务在第三方集成中的通用模式。

架构总览

下图展示从业务调用到数据库事务执行的完整链路,包括开启、提交与回滚的关键节点。

sequenceDiagram
participant S as "业务服务"
participant F as "DB 门面"
participant C as "Connection"
participant P as "连接管理器"
participant DB as "数据库"
S->>F : beginTransaction()
F->>C : 委托开启事务
C->>P : 获取连接资源
P-->>C : 返回连接
C->>DB : 发送 BEGIN
Note over C,DB : 事务开始
S->>F : 多条写入/更新
F->>C : 链式查询/写操作
C->>DB : 执行 SQL
alt 成功
S->>F : commit()
F->>C : 委托提交
C->>DB : 发送 COMMIT
else 异常
S->>F : rollback()
F->>C : 委托回滚
C->>DB : 发送 ROLLBACK
end

详细组件分析

订单定时任务中的批量事务

该任务对符合条件的订单进行批量更新,并在事务内循环处理多表变更,确保原子性。

flowchart TD
Start(["进入方法"]) --> Q["构建查询条件并获取待处理订单集合"]
Q --> T["DB::beginTransaction() 开启事务"]
T --> Loop{"遍历订单"}
Loop --> |是| U1["更新订单主表字段"]
U1 --> U2["更新订单项表字段"]
U2 --> Call["调用自动好评逻辑"]
Call --> Loop
Loop --> |否| Commit["DB::commit() 提交事务"]
Commit --> End(["结束"])
Loop --> |异常| Rollback["DB::rollback() 回滚事务"]
Rollback --> Log["记录回滚错误日志"]
Log --> End

后台订单批量取消事务

后台批量取消订单时,将订单状态、订单项库存锁定状态及关联模块状态在同一事务中更新,异常时回滚并记录日志。

sequenceDiagram
participant Admin as "后台服务"
participant OS as "OrderService"
participant DB as "DB 门面"
participant Conn as "Connection"
participant MySQL as "数据库"
Admin->>OS : cancelAll(ids)
OS->>DB : beginTransaction()
DB->>Conn : 开启事务
loop 遍历订单ID
OS->>DB : 更新订单状态
DB->>MySQL : UPDATE order SET status=...
OS->>DB : 更新订单项库存锁定
DB->>MySQL : UPDATE order_item SET stock_lock=...
OS->>DB : 同步关联模块状态
DB->>MySQL : UPDATE module SET order_status=...
end
alt 无异常
OS->>DB : commit()
DB->>MySQL : COMMIT
else 异常
OS->>DB : rollback()
DB->>MySQL : ROLLBACK
OS->>Log : 记录错误
end

用户微信登录事务

在用户微信登录流程中,插入社交绑定信息、更新登录次数、必要时更新手机号并写入默认联系方式,全部置于同一事务中。

sequenceDiagram
participant API as "WeixinController"
participant DB as "DB 门面"
participant Conn as "Connection"
participant MySQL as "数据库"
API->>DB : beginTransaction()
DB->>Conn : 开启事务
API->>DB : 插入 user_sns 记录
DB->>MySQL : INSERT INTO user_sns ...
API->>DB : 更新 user.login_count
DB->>MySQL : UPDATE user SET login_count=...
alt 需要更新手机号
API->>DB : 更新 user.mobile
DB->>MySQL : UPDATE user SET mobile=...
API->>DB : upsertDefaultContact(...)
DB->>MySQL : UPSERT contact ...
end
API->>DB : commit()
DB->>MySQL : COMMIT

插件 SDK 中的事务封装

插件 SDK 通过 DbHandle 暴露 beginTransaction/commit/rollBack 方法,内部委托连接适配器与 SQL 适配器执行具体语句,体现跨平台事务抽象。

classDiagram
class DbHandle {
+configHandle
+group
+node
+role
+connectionAdapter
+connectionResource
+sqlAdapter
+beginTransaction()
+commit()
+rollBack()
}
class DbConnectionManager {
+getConnection(group, node, role)
-saveConnection(connConf, connection, ttl)
}
DbHandle --> DbConnectionManager : "获取连接"

依赖关系分析

  • DB 门面依赖 Connection 容器单例,所有事务方法最终由 Connection 执行。
  • Connection 通过连接管理器获取底层连接资源,适配不同数据库驱动。
  • 业务服务/控制器通过 DB 门面发起事务,避免直接耦合底层实现。
graph LR
Service["业务服务/控制器"] --> Facade["DB 门面"]
Facade --> Conn["Connection"]
Conn --> Pool["连接管理器"]
Pool --> Driver["数据库驱动"]

性能考量

  • 事务粒度控制:尽量缩小事务范围,仅包裹必要的写操作,减少锁持有时间。
  • 批量操作:如订单定时任务,采用“事务+分批”的方式,避免单次事务过大导致锁竞争与超时。
  • 索引与查询:确保 WHERE 条件有合适索引,减少扫描行数,降低事务内锁冲突概率。
  • 长事务优化:拆分大事务为多个小事务,或使用异步任务处理耗时步骤(如通知、统计)。
  • 并发与锁:在高并发场景下,谨慎使用行级锁(FOR UPDATE),评估死锁风险。
  • 监控与日志:记录事务起止、SQL 执行时间与异常,便于定位瓶颈。

故障排查指南

  • 未提交或回滚:检查是否在异常分支调用了 rollback,并确保正常路径调用了 commit。
  • 事务嵌套:确认是否存在嵌套事务调用,避免重复开启导致状态混乱。
  • 连接失效:检查连接管理器是否成功获取连接,网络抖动或连接池耗尽会导致事务失败。
  • 锁等待与超时:观察慢查询与锁等待日志,优化 SQL 与索引。
  • 回滚失败:捕获并记录回滚异常,确保上层能感知失败并进行补偿。

结论

DouPHP 通过 DB 门面与 Connection 提供了清晰、统一的事务管理能力。业务侧应在服务类中显式开启事务,集中处理相关写操作,并在异常时安全回滚。结合合理的批处理、索引优化与监控日志,可有效提升事务的性能与稳定性。对于复杂场景(嵌套、分布式、长事务),应遵循最小事务原则与幂等设计,必要时引入消息队列或工作流引擎进行解耦与补偿。

附录

开发示例:在服务类中正确使用事务

  • 开启事务:在方法入口处调用 DB::beginTransaction()。
  • 执行业务写操作:按顺序执行多表写入/更新,保持业务语义完整性。
  • 提交事务:所有操作成功后调用 DB::commit()。
  • 异常回滚:在 catch 块中调用 DB::rollback(),并记录错误日志。
  • 隔离级别:如需特定隔离级别,可在 Connection 层配置或通过会话变量设置(例如 READ COMMITTED/REPEATABLE READ)。
  • 性能监控:记录事务起止时间、SQL 列表与影响行数,用于分析与优化。

事务与缓存的一致性保证机制

  • 先写库后删缓存:在事务提交成功后删除或更新缓存,避免脏读。
  • 延迟双删:首次删除缓存后写库,再延时二次删除,降低竞态条件下的不一致。
  • 缓存失效策略:为热点数据设置合理过期时间,作为兜底一致性保障。
  • 事件驱动:通过消息队列广播缓存失效事件,确保多实例一致。

事务失败时的数据恢复策略

  • 幂等设计:确保重试不会造成重复副作用(如基于唯一键或版本号)。
  • 补偿事务:对已部分成功的操作,设计反向操作以恢复一致性。
  • 死信队列:将失败任务转入死信队列,人工介入或自动重试。
  • 审计日志:记录关键状态变更,便于追溯与修复。

常见事务问题与优化建议

  • 问题:长事务导致锁竞争与超时
    • 建议:拆分为多个短事务,或异步化非关键步骤
  • 问题:高并发下死锁
    • 建议:统一加锁顺序,减少跨表锁,优化 SQL
  • 问题:缓存与数据库不一致
    • 建议:先写库后删缓存,配合延迟双删与过期兜底
  • 问题:事务未提交/未回滚
    • 建议:统一 try/catch 模板,确保异常路径必回滚
添加日期:2026-10-05