想象你正在策划一场从东京到京都再到大阪的跨城旅行。你分头订了四样东西:机票、温泉酒店、岚山小火车票、大阪民宿。四份订单、四个独立的商家,每份一旦确认就立即生效,谁也管不着谁。突然台风来了,小火车停运——你必须拿起电话,按相反顺序逐一退订:先退民宿,再退小火车,最后退机票。这个“订了再退”的过程,就是分布式世界里 Saga 长事务模式的缩影。 传统数据库事务讲究“要么全成、要么全败”,可跨微服务的业务流程,根本没法把好几个数据库塞进同一个事务里。Saga 的解法是:把长事务拆成一串独立提交的本地事务,一旦中间某一步失败,就按相反顺序执行补偿操作,让系统最终回到“业务上的一致”。它不求过程瞬间一致,只求终局收敛——就像旅行无论多颠簸,最终都会退回到原点。 本文就用这场旅行的视角,带零基础读者搞懂 Saga 的核心思想、一致性模型、编排式与协同式两种实现风格、补偿与并发设计,以及生产落地指南。
掌握 Saga 长事务模式,你就掌握了跨微服务业务最终一致性的核心设计思路。无论做订单、支付、预订还是 AI Agent 工具编排,面对“一个操作横跨多个服务”的场景,你都能设计出可控的回滚方案——这是资深架构师与初级开发者的分水岭。
可视化演示
观察编排式与协同式Saga的执行流程,以及某步失败时逆序补偿回滚的过程,理解本地事务拆解与最终一致性,可切换三种模式。
本节配有交互动效(进入视口后自动加载,约 3KB)。
编排式Saga:中央协调 协调器按序调用本地事务T1,T2…Tn 某步失败时逆序执行补偿事务 补偿 = C(k-1)…C1 适合复杂、有明确编排的流程。
Saga核心思想
想象你计划一场“东京→京都→大阪”的跨城旅行。机票、温泉酒店、岚山小火车、大阪民宿,是四份完全独立的订单——每份订单一旦确认就真实生效,没有哪个“超级系统”能把它们捆成一份不可拆分的合同。可台风来了,小火车停运,你只能对着已确认的订单,一间间退、一张张退,而且必须严格按相反顺序。
分布式系统里的跨服务业务,就是这个处境。一次订单创建,可能要调用订单服务、库存服务、支付服务、物流服务,四个服务各有各的数据库,根本无法放进同一个数据库事务。1987 年,Hector Garcia-Molina 发表了 Saga 模式的经典论文:把长事务拆成一串独立提交的本地事务,不求“同生共死”,只求失败时能“有序退场”。
一个 Saga 由正向执行序列 T1→T2→……→Tn 和配套的补偿序列 C1→C2→……→C(n-1) 组成。每个 Ti 是真实提交的本地事务;当 Tk 失败时,Saga 放弃后续事务,转而依次执行 C(k-1)→……→C1。这里有个关键认知:补偿不是回滚——Ti 已经 COMMIT,数据已对外可见,C_i 必须是一笔新的、能抵消 Ti 业务后果的动作。比如“扣款”的补偿是“退款”,“扣库存”的补偿是“回补库存”。
回到旅行:小火车停运后,你退民宿、退温泉酒店、退机票,每一步都是真实发生的“反向订单”。Saga 的哲学正源于此——不假装什么都没发生,而是承认每个已提交的事务,再用一系列新的业务动作,让整体状态回到“好像这趟旅行没发生过”。
- 正向执行序列T1 到 Tn 按顺序逐个提交,每个本地事务独立落库、独立生效
- 失败条件第 k 个正向事务返回失败(或超时),后续事务不再执行
- 逆向补偿序列从 T(k-1) 的补偿开始,一路回退到 C1,完成整体回滚
一致性模型
订四份旅行订单时,你永远不会看到“四份同时确认”的瞬间。可能机票已出票、酒店显示“处理中”、小火车“出票中”、民宿还在等支付回调。整个过程中,系统对外呈现的是一堆相互矛盾的中间状态——这不是故障,而是分布式世界的常态。
传统 ACID 事务要求原子性(Atomicity)和隔离性(Isolation),但跨服务场景受 CAP 定理约束:网络分区时,必须在“可用性”和“一致性”之间二选一。Saga 选择拥抱 BASE——基本可用(Basically Available)、软状态(Soft state)、最终一致(Eventually consistent)。它放弃了 ACID 的原子性,也放弃了隔离性。
在 Saga 中,任何时刻,外部观察者都可能看到“部分提交、部分未提交”的中间状态。系统承诺的是:只要正向链路全部成功,或补偿链路执行到底,状态就会收敛到某个确定的目标。这个从不一致到一致的“收敛过程”,就是最终一致性。设计业务时要让上层容忍这种中间态,比如订单页允许出现“支付中”。
就像旅行途中你不断刷手机,看到订单状态七零八落,但旅行结束后所有订单要么全部确认过、要么全部退款到账,页面不再有任何“处理中”。Saga 保证的是终局,而不是过程。
- 极限符号表示“随着 t 不断增大,结果无限逼近某个值”
- 状态距离函数度量当前系统状态与目标状态的差异程度,差异越大值越大
- 收敛至零当到达收敛时间 T_conv 后距离为 0,即完全一致
编排式实现
你不想自己打四通电话,于是委托了一位旅行管家。管家手里有一张行程单,按顺序打电话:先确认机票,再订酒店,再订小火车。哪一步出问题,管家立刻按相反顺序打退订电话,而你只需要跟管家一个人沟通,出了纠纷也有一个人可以追责。
这就是编排式(Orchestration)Saga。系统中有一个中央协调器(Orchestrator),它知道完整流程,向各个服务发送“执行指令”,再根据返回结果决定下一步:继续执行 T(i+1),还是转入补偿链路 C(i-1)→……→C1。每个 Saga 实例在协调器里都有明确的状态,例如 INIT、T1_DONE、T2_FAILED、COMPENSATING、FINISHED。
协调器通常用状态机实现:每收到一个事件,状态机做一次转移,并持久化当前状态以便崩溃恢复。优点是控制集中、流程显式、容易调试和监控;缺点是协调器承载全部流程逻辑,可能成为单点,需要高可用部署,且服务间多为同步调用,耦合度偏高。
旅行管家把所有行程记在笔记本上,你从不操心细节。编排式的价值就在于“有一个看得见全流程的大脑”,出了任何问题都可以摊开笔记本复盘:哪一步、为什么、补偿了没有、卡在哪个服务。
- 当前状态 S_kSaga 实例此刻所处的阶段,如“T2 已提交”
- 触发事件 E_k某服务返回的成功、失败或超时消息
- 转移函数根据当前状态和事件决定下一状态,如“T2 成功”→“执行 T3”,“T3 失败”→“补偿 T2”
协同式实现
另一种风格是“接力棒式旅行”:没有管家,酒店前台确认完你的入住后,顺手给旅行社发条消息:“客人到了,请安排明天的行程。”旅行社完成后,再给民宿发消息:“行程已安排,请备好房间。”如果某站发现接待不了,就一路往回传话取消。指挥权不在任何人手里,而在消息本身。
这就是协同式(Choreography)Saga:没有中央协调器,各服务通过事件总线异步交互。每个服务订阅自己关心的事件,完成本地事务后发布新事件,下一个服务被事件“唤醒”继续执行,形成一条事件链。
协同式的优点是服务间松耦合、无单点、扩展性好,适合高并发事件流场景;缺点是流程逻辑散落在各服务的事件处理器中,全局视图缺失,排查问题必须依赖链路追踪(如 OpenTelemetry)。事件可靠性要依赖消息队列的“至少一次投递”加消费者的幂等处理,事件乱序还要用版本号或状态机约束。
接力棒的弊端也明显:如果中途某棒手忘了传棒,你必须沿着整个赛道挨个询问“你收到消息了吗”。协同式 Saga 排查问题,就得靠完善的链路追踪,否则一次“事件丢失”可能让整个旅行悄无声息地停摆。
- 订阅事件服务 Si 从事件总线接收上一个服务发布的事件 E(i-1)
- 执行本地事务Si 在自己的数据库里完成 Ti 并提交
- 发布新事件成功后 Si 向总线发布 E_i,唤醒下一个服务
补偿事务设计
退订听起来简单,但处处是坑。你以为给酒店打了退款电话就万事大吉?可能酒店系统重复执行了两次退款;可能你的入住其实没订上(系统超时但没成功),退了个寂寞;还可能你退款刚打完,酒店又收到了旅行管家的“确认入住”消息——先退后订,顺序全乱了。
补偿事务设计有四个公认要点:反向操作、幂等设计、空补偿、悬挂处理。反向操作要求 C_i 是能真实抵消 T_i 副作用的新业务,比如“发货”的补偿是“拦截发货”,而不是“删除发货记录”——因为记录已经被下游系统消费了。
幂等设计是补偿的基石:消息可能重复投递、重试可能重复触发,补偿接口必须通过业务唯一键(如 refund_id)保证执行多次与执行一次结果相同。空补偿指 T_i 实际没提交成功时,补偿逻辑要能识别并跳过,避免“退了一个不存在的订单”。悬挂处理指补偿先于正向事务到达,需要用状态字段拦截后续的正向操作,防止“补完又订”。
好的退订系统就像酒店前台的“退订编号”:一个订单一个退订单号,重复打来电话就说“您已退订过了”。设计补偿事务时,脑子要时刻装着这个场景——重点不是“怎么退”,而是“怎么退都不会出错”。
- 补偿函数对业务状态 x 执行一次反向操作,得到 x'
- 二次调用对结果再执行一次相同的补偿操作
- 幂等成立第二次补偿后状态不再变化,x'' = x',重复执行无害
并发控制策略
旅行中还有个经典冲突:你和另一位游客同时订同一班航班的最后一个座位。你们都在付钱,都以为订到了。更糟的是,你退票的同时,对方正在值机。没有并发控制,退订与入住就会互相踩踏。
Saga 不提供隔离性,多个 Saga 实例并发操作同一份数据时,可能产生脏读、脏写。常见策略有三类:乐观锁(版本号 + CAS)、悲观锁(分布式锁)、业务状态校验。乐观锁适合冲突少的场景:每次更新带上 version 条件,更新成功才继续;悲观锁(Redis/etcd 分布式锁)适合冲突激烈的场景,但要注意锁的持有时间与续期。
业务状态校验是最后一道防线,比如“只有当订单状态为 PAID 时才允许执行补偿”,从业务语义上拦截非法流转。三种手段可以叠加使用:状态校验兜底,乐观锁提高并发,悲观锁保护稀缺资源。
旅行社的“锁座”机制就是乐观锁的日常版:你选座时系统记下“该视图已被锁定,版本号 +1”,谁先提交谁成功。并发控制不是 Saga 的目标,但它是 Saga 能安全运行的护栏——没有护栏,再完美的退订流程也会被同时发生的另一场旅行撞得七零八落。
- 两两组合数n 个并发事务中任取两个发生竞争的组合数
- 单对冲突概率任意一对事务对同一数据发生写冲突的概率为 p
- 总冲突概率组合数乘以单对概率的平方项,事务越多冲突概率越大
生产实践指南
成熟的旅行社不会只在旺季卖行程。它会预判台风季(超时预案)、给每个订单挂状态标签(监控大屏)、淡季时演练全套退票流程(故障演练)。Saga 要上生产,同样需要这些“非功能”功夫。
首先是适用场景:Saga 适合跨多个服务、流程较长、对实时一致性要求不高的业务,比如订单、预订、审批流。如果业务要求强一致(如账户余额实时扣减),Saga 并不合适,应考虑 TCC 或其他方案。
生产落地四件事:一,每个本地事务与补偿都设超时,超时按失败处理并触发重试;二,为补偿链路设置重试次数上限与退避策略,防止补偿风暴打垮下游;三,监控每个 Saga 实例的状态分布(执行中/失败/补偿中/终态),统计补偿成功率;四,定期做故障演练,人为注入服务故障,验证补偿链路能真正恢复。
每一次旅行都是对旅行社组织能力的考验。一个把退票流程演练过一百遍的管家,台风天依然从容。Saga 在生产环境的价值,不在它“设计得漂亮”,而在它“退得干净、看得清楚、扛得住故障”。
- 连乘符号n 个事务全部成功的概率等于各自成功率相乘
- 指数近似当每个 p_i 都接近 1 时,乘积近似为指数形式
- 工程含义单环成功率 99% 时,10 环 Saga 整体仅约 90.4%
AI 应用场景
AI 旅行规划 Agent 的多工具编排
阶段:Agent 工具调用与错误恢复
解决的问题:传统方式下,AI Agent 逐个调用订票、订酒店、订车 API,中途某个工具失败后,已成功的预订成为“孤儿资源”,用户被迫手动取消。AI 方式:借鉴 Saga,Agent 维护工具调用链路,失败时反向调用取消工具,实现自动补偿。
具体案例:用户让 AI 助手规划“北京→上海→杭州”出差行程。Agent 先调用机票 API 出票,再调用酒店 API 订房,最后调用高铁 API 时发现余票不足。此时 Agent 自动执行补偿:调用酒店退订 API、机票退票 API,并向用户反馈“行程已自动回滚,建议调整出行时间”。
跨行转账的清算补偿
阶段:智能风控决策链路的分布式事务
解决的问题:银行间转账涉及扣款、清算、入账多个独立系统,传统强事务无法跨行加锁。Saga 将转账拆成“扣款→清算→入账”,任一步失败即触发退款补偿,保证资金最终一致。
具体案例:用户从 A 行向 B 行转账 5000 元,A 行扣款成功,B 行入账时因账号异常失败。Saga 补偿流程被触发:A 行自动发起退款,用户资金在几分钟内回到原账户,账单页短暂显示“转账处理中”,最终收敛为“转账失败,已退款”。
订单系统的可观测性运维
阶段:智能化根因分析与故障自愈
解决的问题:订单创建涉及库存、支付、积分、物流多个微服务,任一环节超时都会拖垮整体成功率。用编排式 Saga + 监控大盘,将失败率、补偿成功率指标化,接入 AI 告警分析自动定位故障根因。
具体案例:某电商大促期间,库存服务 RT 飙升至 5s,订单 Saga 失败率从 0.1% 升到 3%。监控系统通过 Saga 实例状态分布发现大量实例卡在“补偿中”,AI 根因分析定位到库存服务连接池耗尽,运维一键扩容后,补偿链路自动重试并收敛,用户侧订单状态全部恢复正常。
Python 演示
import randomimport time class SagaOrchestrator: """编排式 Saga:正向执行 + 逆向补偿""" def __init__(self): self.step_list = [] # 正向步骤: [(名称, 事务函数)] self.comp_map = {} # 步骤名 -> 补偿函数 def add_step(self, name, tx_func, comp_func): self.step_list.append((name, tx_func)) self.comp_map[name] = comp_func def run(self): done = [] for name, tx_func in self.step_list: try: tx_func() done.append(name) print("[正向] {} 提交成功".format(name)) except Exception as exc: print("[失败] {} 执行出错: {}".format(name, exc)) self._compensate(done) return False print("[完成] 所有事务提交成功") return True def _compensate(self, done): for name in reversed(done): comp_func = self.comp_map[name] try: comp_func() print("[补偿] {} 已回退".format(name)) except Exception as exc: print("[告警] {} 补偿失败: {}".format(name, exc)) def book_flight(): print(" 订机票:北京→上海 已出票") def cancel_flight(): print(" 退机票:票款 1200 元原路退回") def book_hotel(): print(" 订酒店:外滩行政房 已确认") def cancel_hotel(): print(" 退酒店:房费 800 元原路退回") def book_train(): raise RuntimeError("高铁余票不足") def cancel_train(): print(" 退高铁票:无出票记录,空补偿跳过") def main(): saga = SagaOrchestrator() saga.add_step("机票", book_flight, cancel_flight) saga.add_step("酒店", book_hotel, cancel_hotel) saga.add_step("高铁", book_train, cancel_train) ok = saga.run() print("最终一致性结果:", "行程全部确认" if ok else "行程已自动回滚") if __name__ == "__main__": main()
self.step_list = []保存正向步骤列表,self.comp_map = {}保存每个步骤对应的补偿函数,两个集合属性与方法无同名冲突def add_step(self, name, tx_func, comp_func)注册一个本地事务及其配套补偿,类似旅行管家往行程单里加一站for name, tx_func in self.step_list:按注册顺序执行正向链路,对应 Saga 的 T1→T2→T3except Exception as exc:捕获任一事务异常,立即跳出正向循环转入补偿,对应“Tk 失败则停止正向执行”self._compensate(done)触发逆向补偿流程,对应公式中的 C(k-1)→……→C1for name in reversed(done):按已提交列表的相反顺序逐一补偿,这是 Saga 逆向回退的核心raise RuntimeError("高铁余票不足")模拟第 3 个本地事务失败,驱动演示进入补偿分支ok = saga.run()执行整条 Saga 并返回是否全部成功,业务侧据此判断“全确认”还是“已回滚”
常见误区
误区Saga 的补偿不就是把数据库回滚到事务开始前的状态吗?跟 ACID 的 rollback 一样,只是跨了多个库而已。
正解完全不是。本地事务已经 COMMIT,数据已对外可见,数据库的回滚机制早已失效。补偿是执行一笔新的业务动作去“撤销”已提交事务的业务影响:扣款了就去退款,发短信了就发取消短信,发货了就拦截物流。这是业务层面的反向操作,不是技术层面的日志回滚。
为何常见:因为“补偿”和“回滚”都表达“挽回”的意思,初学者容易从字面理解,忽略了分布式场景下 COMMIT 不可撤销的本质。
误区Saga 是一套完整的事务方案,所以执行过程中数据也应该像数据库事务一样,任何时刻对外保持一致。
正解Saga 只保证最终一致性,不保证过程一致性。执行期间,不同服务看到的数据可能互相矛盾(机票已出、酒店还在处理),外部系统可能短暂读到中间状态。设计时要让业务容忍这种中间态,并准备好补偿链路,而不是期待过程一致。
为何常见:习惯了 ACID 事务“要么全有、要么全无”的语义,容易把 Saga 也当成强一致方案,忽略了 CAP 定理下跨服务场景根本无法做到强一致。
误区协同式没有中心控制,流程乱糟糟的;编排式有统一协调器,流程清晰,所以肯定选编排式。
正解各有适用场景。编排式控制集中、易调试,但协调器是单点和逻辑瓶颈,且服务间同步调用耦合度高。协同式异步解耦、无单点,但流程分散,排查问题依赖链路追踪。高并发事件流场景反而更适合协同式。选型要看业务对控制力与吞吐量的权衡。
为何常见:集中式控制符合人类“有个负责人”的直觉,容易低估它带来的耦合与单点风险。
误区只要给每个事务配上补偿函数,Saga 就可靠了,剩下的交给框架就行。
正解补偿代码本身可能失败:网络抖动、服务宕机、消息重复、数据被并发修改。生产环境必须叠加幂等、重试、超时、监控告警、人工兜底。补偿成功率不可能 100%,要把“补偿也失败”当作常态来设计,而不是意外。
为何常见:把补偿想成“调用一个函数”这么简单,忽略了分布式环境下任何一次调用都可能失败,且失败后还可能被重复调用。
误区只要是分布式事务问题,上 Saga 就对了,它是分布式事务的万能解。
正解Saga 只适用于“长流程、可补偿、弱一致可接受”的业务。像账户余额实时扣减这类强一致场景,需要 TCC(预留+确认+取消)或本地消息表等方案;短平快的强一致场景也可考虑两阶段提交。选型要看业务对一致性的真实要求,而不是追热点。
为何常见:因为 Saga 是热门概念,容易被当成“万能钥匙”,忽略了分布式事务方案各有适用边界的本质。
面试题
Q1. 在 Saga 模式中,“补偿”操作的本质是什么?
- A.重新执行一次正向事务的重试机制
- B.执行一笔新的反向业务操作来抵消副作用← 正确答案
- C.利用数据库的 undo 日志恢复历史数据
- D.锁定所有资源等待故障恢复后再提交
答案:B。补偿不是回滚也不是重试。Ti 已经提交,数据库回滚机制失效,必须用一笔新的业务动作(如退款抵消扣款)来抵消副作用。A 是重试,C 是回滚,D 是强同步方案,均不符合补偿语义。
Q2. 编排式 Saga 与协同式 Saga 最核心的区别是?
- A.是否使用消息队列进行异步通信
- B.是否存在一个中央协调器控制流程← 正确答案
- C.是否要求所有服务提供幂等接口
- D.是否使用分布式锁控制并发访问
答案:B。编排式有一个 Orchestrator 中央协调器集中控制流程;协同式没有中心节点,靠事件驱动。A 项协同式常用但并非本质区别,C 项两种风格都需要幂等,D 项与并发控制相关,均非核心差异。
Q3. Saga 模式提供的一致性保证属于?
- A.线性一致性(强一致)
- B.最终一致性(弱一致)← 正确答案
- C.因果一致性(偏序关系)
- D.会话一致性(自洽读取)
答案:B。Saga 执行过程中数据存在中间不一致状态,系统承诺最终收敛到一致,属于最终一致性。跨服务场景下 ACID 的强一致无法保证,因果一致与会话一致是另外两种一致性模型,与 Saga 的承诺不符。
Q4. 关于 Saga 的执行过程,下列说法正确的是?
- A.通过两阶段锁保证全局隔离性
- B.每个事务独立提交且不可回滚← 正确答案
- C.任一时刻外部观察者看到一致
- D.自动使用数据库日志恢复数据
答案:B。Saga 的每个 Ti 是独立提交的本地事务,提交后不可回滚,只能靠补偿。A 错在 Saga 不提供全局隔离;C 错在过程允许中间不一致;D 错在补偿是业务动作而非日志恢复。
Q5. 协同式 Saga 中,服务之间通过什么机制衔接流程?
- A.分布式锁与共享内存通信
- B.事件发布与订阅的异步消息← 正确答案
- C.中央协调器的同步 RPC 调用
- D.数据库外键与关联查询
答案:B。协同式没有协调器,服务通过事件总线发布/订阅消息衔接:完成本地事务后发布事件,触发下一个服务。A 是并发控制手段而非流程衔接,C 是编排式做法,D 是单体应用的做法。
Q6. 一个 Saga 包含 10 个本地事务,若每个事务成功率均为 99%,则整体成功率约为?
- A.99.0%
- B.95.1%
- C.90.4%← 正确答案
- D.85.7%
答案:C。整体成功率 = 0.99^10 ≈ 0.9044,约 90.4%。这说明链路越长整体成功率掉得越快,补偿能力是 Saga 的必选项。A 忽略了连乘效应,B 更接近 0.99^5,D 更接近 0.99^15。
Q7. 补偿事务设计中,需要“空补偿”机制针对的场景是?
- A.补偿函数执行多次导致数据被重复修改
- B.正向事务实际未提交却触发了补偿流程← 正确答案
- C.补偿操作先于正向事务到达产生错乱
- D.多个并发 Saga 同时补偿同一个事务
答案:B。空补偿处理“Ti 因超时等原因实际未提交成功,但 Saga 已进入补偿流程”的情况,补偿逻辑要识别并跳过,避免“退了一个不存在的订单”。A 是幂等问题,C 是悬挂问题,D 是并发控制问题,三者与空补偿是不同维度的设计要点。
Q8. 下列哪些属于 Saga 补偿事务设计的核心要点?
- A.补偿必须是可重复执行的幂等操作← 正确答案
- B.补偿需识别并处理空补偿场景← 正确答案
- C.补偿需保证事务强一致隔离
- D.补偿先于正向到达需悬挂处理← 正确答案
答案:ABD。幂等、空补偿、悬挂处理是补偿设计公认的三大要点。C 错误:Saga 根本不提供强一致隔离,补偿的目标是业务终态收敛,而非 ACID 隔离。
Q9. 为 Saga 引入并发控制策略时,常用的手段有?
- A.乐观锁:版本号加比较交换更新← 正确答案
- B.悲观锁:分布式锁保护临界资源← 正确答案
- C.业务状态校验:更新带上前置状态← 正确答案
- D.全局互斥:所有 Saga 串行化执行
答案:ABC。乐观锁、悲观锁、业务状态校验是常见手段,可以叠加使用。D 错误:全局串行化会彻底抹杀分布式系统的并发能力,牺牲可用性换取一致性,与 Saga 的设计初衷相悖,不是可行策略。
Q10. 关于编排式与协同式 Saga 的对比,正确的说法是?
- A.编排式协调器可能成为单点瓶颈← 正确答案
- B.协同式流程分散难以全局排障← 正确答案
- C.编排式完全不需要幂等补偿设计
- D.协同式依赖可靠的事件总线← 正确答案
答案:ABD。编排式协调器承载全部流程逻辑,确实有单点风险;协同式流程散落在各服务中,排障依赖链路追踪;协同式的事件驱动模式依赖消息总线的可靠性。C 错误:两种实现风格都必须做幂等补偿设计,幂等性与实现风格无关。