2026/4/17 9:47:20
网站建设
项目流程
西安道桥建设有限公司网站,精准营销公司,广告设计与制作专业怎么样,小程序推广引流幂等性设计详细介绍
本文介绍了幂等性设计的概念和重要性。幂等性指同一接口多次调用产生的结果与单次调用一致#xff0c;是支付、发货等关键接口的必要特性。文章分析了非幂等设计可能导致的问题#xff0c;如重复扣款、数据不一致等#xff0c;并指出前端防护的局限性。…幂等性设计详细介绍本文介绍了幂等性设计的概念和重要性。幂等性指同一接口多次调用产生的结果与单次调用一致是支付、发货等关键接口的必要特性。文章分析了非幂等设计可能导致的问题如重复扣款、数据不一致等并指出前端防护的局限性。详细阐述了幂等设计的四个原则服务端保证、时效管理、结果一致性和可追溯性。重点介绍了四种主流实现方案数据库唯一索引含雪花算法详解、乐观锁、天然幂等操作和分布式锁分析了各方案的适用场景、实现要点及注意事项为系统设计提供了全面的幂等性解决方案参考。何为幂等性?使用相同参数来调用同一接口调用多次的结果跟单次产生的结果是一致的。一些关键接口都需要幂等设计比如支付扣款、发货等等。否则可能导致重复扣款重复发货数据不一致用户体验差甚至导致用户流失还有可能是用户误触的比如多次点击按钮导致多次提交等。虽然前端可以通过将按钮置灰防止重复点击但是纯前端无法完美实现幂等性。比如前端调用后端接口超时有可能后端已经存储了数据此时前端的按钮已经可点击用户再次点击就会生成两条数据。设想一下可能由于网络问题我们调用扣款接口超时了并且没有进行重试这样有可能给用户发货了但是实际没扣款因此这种情况下通常要重试扣款。但是如果重试了假设之前超时的那次调用实际是成功了只是响应结果的时候接口超时了这样就重复扣款两次肯定是不行的。//物流服务 { //调用订单服务 doOrder }catch{ //超时了那么再调一次 do0rder } // 订单服务 doorder { // 执行扣款 money-- // 记录日志1og时程序超时了 }所以幂等设计在一些必须要保证业务一致性的情况下非常关键因为这种场景往往需要重试重试就需要幂等。为每个可能重复的操作分配全局唯一的标识符通过该标识符识别重复请求。这是幂等设计的基础理念如同为每笔交易赋予独一无二的身份证号。幂等设计原则原则一客户端无关性幂等性必须由服务端保证不能依赖客户端的任何行为如按钮置灰、页面跳转。客户端可能崩溃、被用户绕过、或存在多个入口。原则二时效性考虑幂等标识需要合理的生命周期管理。过短会导致合法重试被拒绝过长则占用过多存储资源。通常根据业务特征设置数分钟到数天的有效期。原则三错误处理一致性对于已处理的重复请求应返回与首次成功相同的结果而不是抛出重复操作错误。这对用户体验至关重要。原则四可追溯性必须记录幂等处理日志包含请求标识、处理状态、时间戳等信息便于问题排查和数据审计。幂等设计---4种主流方案一、数据库唯一索引实现原理利用数据库的唯一索引特性在数据层阻止重复记录的插入。适用场景核心业务数据的创建操作能够生成全局唯一标识的业务对性能要求较高的高频操作注意点插入冲突时数据库会抛出异常需在应用层优雅处理在高并发场景下大量冲突可能导致数据库连接池压力分布式数据库环境下需要确保唯一索引的全局性比较常见的一种是使用UUID来生成唯一id一种是使用雪花算法。生成随机 UUID 字符串并且在数据库中新增一列唯一索引存储 UUID。但其实没必要新增一列因为表里面的主键本身就是唯一的所以可以复用主键来进行唯一性判断。因为主键的类型是 bigint所以只需要更换唯一 id 生成的策略使用雪花算法来生成分布式全局唯一的自增 id 即可。雪花算法可以使用 Hutool 工具类提供的工具类来基于雪花算法生成 idIdUtil.getSnowflakeNextId()雪花算法原理当需要生成一个新ID时算法按以下步骤进行首先获取当前时间戳毫秒级减去预设的纪元时间得到时间差值。检查这个时间差值与上次生成ID的时间戳的关系。如果当前时间戳小于上次时间戳说明发生了时钟回拨需要特殊处理比如抛出异常或等待。如果当前时间戳等于上次时间戳说明是在同一毫秒内那么序列号自增1。如果序列号达到最大值4095则等待到下一毫秒再生成。如果当前时间戳大于上次时间戳说明进入了新的毫秒序列号重置为0。然后将这四个部分通过位运算组合起来将时间戳部分左移到对应位置机器标识部分左移到对应位置序列号放在最低位最后进行或运算得到最终的64位ID。雪花算法特性雪花算法生成的ID具有全局唯一性因为不同机器有不同的机器标识相同机器在不同时间有不同时间戳同一机器同一时间有不同序列号。ID是趋势递增的因为时间戳在高位新生成的ID比旧生成的ID数值大这对数据库索引友好。算法在本地生成ID不需要网络通信性能很高。雪花算法的核心就是通过时间戳(毫秒)保证递增通过机器id、服务 id 和递增序号(同一毫秒内递增)保证唯一性。时钟回拨问题解决时钟回拨问题的6种常见方法等待时钟追上当检测到时钟回拨时让线程等待直到时间追赶上最后一次生成ID的时间。这种方法适用于回拨时间很短的场景比如毫秒级或秒级。但是如果回拨时间较长等待时间也会很长影响系统可用性。使用扩展位记录回拨在ID中预留几位比如1-2位作为回拨计数。当发生时钟回拨时将回拨计数加1并仍然使用旧的时间戳或回拨后的时间戳生成ID通过回拨计数来区分。这样即使时间戳相同回拨计数不同ID也不同。但是这种方法会减少时间戳或序列号的位数影响ID的生成时长或并发能力。异常报警人工处理当检测到时钟回拨时抛出异常并记录日志通知运维人员处理。这种方法适用于对时钟回拨非常敏感且不可容忍的场景但依赖人工介入实时性差。使用备用时间源不使用本地机器时钟而是使用独立的、可靠的时间服务如GPS时钟、原子钟等。但这会增加系统复杂性和成本。缓存历史时间戳在内存或外部存储中保存最近一段时间使用过的时间戳当时钟回拨时从缓存中取出一个尚未使用过的时间戳或者使用回拨后的时间戳但通过其他方式保证唯一性。不过这种方法实现复杂且可能带来性能问题。调整雪花算法结构例如美团的Leaf算法对雪花算法进行了改进使用Zookeeper或数据库来分配workerId并且在发生时钟回拨时使用预留的位来记录回拨次数从而保证ID的唯一性。二、数据库乐观锁实现原理通过版本号或时间戳字段确保数据更新操作的原子性和一致性。适用场景资源数量有限的更新操作如库存扣减需要记录完整变更历史的业务并发冲突概率较低的场景1. 读取数据时获取当前版本号 2. 更新时附带版本号条件WHERE id ? AND version ? 3. 检查更新影响行数0行表示版本已变更操作需重试或放弃优势避免悲观锁的性能开销天然支持重试机制提供数据变更的追溯能力局限不适合极高并发的争抢场景需要应用层处理更新失败逻辑可能增加数据库查询次数三、天然幂等操作比如一些 delete 操作这种是天然幂等的因为删除一次和多次都是一样的。还有一些更新操作例如:update sys config set configa where id 1;这样的 SQL不论执行几遍结果都是一样的。如果接口里面仅包含上述的这些天然幂等的行为那么对外就可以标记当前接口为幂等接口不需要任何其他操作。四、分布式锁实现原理通过分布式锁确保同一业务键的操作在任意时刻只有一个执行线程。适用场景涉及多个数据源更新的复杂事务无法仅通过数据库约束保证一致的业务需要执行额外业务逻辑校验的场景实现要点锁粒度要适中太粗影响并发太细增加复杂度必须有锁超时机制防止死锁建议采用Redisson等成熟框架避免自研坑点1. 根据业务特征生成锁键如order:pay:{orderId} 2. 尝试获取分布式锁设置合理超时时间 3. 在锁保护下执行幂等检查和业务操作 4. 无论成功失败最终必须释放锁方案选型