幂等性情况
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)在事务中执行:
- 系统开始一个事务。
- 从发送方账户扣除资金。
- 将相同金额添加到接收方账户。
- 提交事务。
如果所有步骤都成功,事务将永久保存。提交后,查询数据库会正确显示更新后的余额。
系统故障时会发生什么?
提交前的故障
如果系统在事务提交之前崩溃,则所有更改都不会被保存。数据库会自动回滚事务,确保不会出现部分更新的情况。这可以防止出现扣款却未到账的情况。
提交后的故障
如果系统在提交后立即崩溃,变更仍然会被持久化。当数据库重新启动时,更新后的余额保持不变。这种行为体现了 Durability(持久性),即一旦事务提交,就不会丢失。
数据库如何确保持久性
现代数据库(如 PostgreSQL)使用一种称为 Write‑Ahead Logging (WAL) 的机制。在对实际数据进行任何更改之前,数据库会先将这些更改记录在日志文件中。若发生崩溃,系统会重放该日志,以恢复最新的已提交状态。这确保了已提交的事务能够在意外故障中存活下来。
处理转账中的幂等性
在真实的支付系统中,由于网络重试、超时或客户端错误,单个交易请求可能会被发送多次。如果处理不当,这会导致重复的资金转账,进而产生不正确的余额和财务不一致。
模拟重复交易
考虑一种场景:从 Alice 向 Bob 转账 ₹200 被执行了不止一次:
- 第一次执行: Alice → 800,Bob → 700
- 第二次执行(重复): Alice → 600,Bob → 900
同一操作被反复应用,导致意外的扣款和入账。
问题观察
数据库会独立处理每个请求。若没有额外的防护措施,它无法识别交易是否已经执行过。因此,重复请求会导致重复更新。
为什么这很危险
- 用户可能被多次扣费。
- 账户余额变得不准确。
- 系统的信任度下降。
实际系统如何防止
- 唯一交易 ID – 为每笔交易分配唯一标识符。处理前,系统检查该 ID 是否已存在;若已存在,则忽略该请求。
- 幂等键(Idempotency Keys) – 客户端在每次请求中发送唯一键。服务器存储该键,并确保使用相同键的重复请求产生相同结果,而不重新处理。
- 数据库约束 – 可以在交易标识符上设置唯一约束或索引,以在数据库层面防止重复条目。
事务日志
维护事务历史有助于验证请求是否已经完成。