幂等性情况

发布: (2026年3月30日 GMT+8 01:22)
6 分钟阅读
原文: Dev.to

I’m ready to translate the article for you, but I need the full text you’d like translated. Could you please paste the content (excluding the source line you already provided) here? Once I have it, I’ll translate it into Simplified Chinese while preserving all formatting, markdown, and technical terms.

确保使用数据库事务进行可靠的资金转账

在类似 PhonePe、Google Pay 或 Paytm 的数字钱包系统中,用户期望他们的资金能够被准确且安全地处理。即使是微小的不一致——例如从一个账户扣款而未向另一个账户记入——也可能导致严重的财务问题。为防止此类问题,数据库系统依赖 ACID properties,尤其是 Durability,以保证交易的安全性和一致性。

系统概述

系统维护一个 accounts 表,每个用户都有余额。用户可以:

  • 将钱存入钱包
  • 向其他用户转账
  • 查看交易历史

为确保正确性,数据库强制执行诸如非负余额和时间戳跟踪等规则。

执行安全转账

一个典型的用户之间的资金转账(例如,Alice 到 Bob)在事务中执行:

  1. 系统开始一个事务。
  2. 从发送方账户扣除资金。
  3. 将相同金额添加到接收方账户。
  4. 提交事务。

如果所有步骤都成功,事务将永久保存。提交后,查询数据库会正确显示更新后的余额。

系统故障时会发生什么?

提交前的故障

如果系统在事务提交之前崩溃,则所有更改都不会被保存。数据库会自动回滚事务,确保不会出现部分更新的情况。这可以防止出现扣款却未到账的情况。

提交后的故障

如果系统在提交后立即崩溃,变更仍然会被持久化。当数据库重新启动时,更新后的余额保持不变。这种行为体现了 Durability(持久性),即一旦事务提交,就不会丢失。

数据库如何确保持久性

现代数据库(如 PostgreSQL)使用一种称为 Write‑Ahead Logging (WAL) 的机制。在对实际数据进行任何更改之前,数据库会先将这些更改记录在日志文件中。若发生崩溃,系统会重放该日志,以恢复最新的已提交状态。这确保了已提交的事务能够在意外故障中存活下来。

处理转账中的幂等性

在真实的支付系统中,由于网络重试、超时或客户端错误,单个交易请求可能会被发送多次。如果处理不当,这会导致重复的资金转账,进而产生不正确的余额和财务不一致。

模拟重复交易

考虑一种场景:从 Alice 向 Bob 转账 ₹200 被执行了不止一次:

  • 第一次执行: Alice → 800,Bob → 700
  • 第二次执行(重复): Alice → 600,Bob → 900

同一操作被反复应用,导致意外的扣款和入账。

问题观察

数据库会独立处理每个请求。若没有额外的防护措施,它无法识别交易是否已经执行过。因此,重复请求会导致重复更新。

为什么这很危险

  • 用户可能被多次扣费。
  • 账户余额变得不准确。
  • 系统的信任度下降。

实际系统如何防止

  • 唯一交易 ID – 为每笔交易分配唯一标识符。处理前,系统检查该 ID 是否已存在;若已存在,则忽略该请求。
  • 幂等键(Idempotency Keys) – 客户端在每次请求中发送唯一键。服务器存储该键,并确保使用相同键的重复请求产生相同结果,而不重新处理。
  • 数据库约束 – 可以在交易标识符上设置唯一约束或索引,以在数据库层面防止重复条目。

事务日志

维护事务历史有助于验证请求是否已经完成。

0 浏览
Back to Blog

相关文章

阅读更多 »

耐久性

Durability 意味着一旦 transaction 成功 committed,其 changes 永久保存于 database 中。即使系统崩溃,...

CA 36 – 隔离 (ACID)

场景:该实验演示了 ACID 中 Isolation(隔离)属性在两个会话尝试并发操作同一账户时的工作方式。步骤:会话…