> 古语云:“一而再,再而三,其效不二” > 俗语讲:被虐千百遍,依然如初恋 > 数学符号:f(f(f(x))) = f(x) > 即无论操作执行一次还是多次,其效果始终如一,不会有差异。这就是幂等性。 接口幂等性就是用户对于 `同一操作`发起的 `一次请求或者多次请求的结果是一致`的,不会因为多次点击而产生了副作用。 > 注意:数据库可能产生幂等性问题,但是幂等性问题不只发生在数据库。 比如在订单系统中,订单可以直接支付、积分抵扣、余额支付等不同的支付方式,除直接支付外,其他资产在获取的过程中可能存在以下过程:  由于网络存在不稳定的因素,这个通知可能会发送多次,极端请况下,同一笔订单的多次通知可能会同时到达商城服务端,如果不做幂等,那么同一笔订单就可能被多次处理,继而出现多次发放、ABA等一系列并发问题。 ## 如何产生幂等问题 1. `网络请求重试`:网络波动或超时,客户端可能会重复发送相同的请求。 2. `用户界面重复提交`:用户在用户界面上可能会不小心重复点击按钮,导致仙童的请求多次发送。 3. `消息队列重试机制`:使用消息队列时,消息可能会被重复消费。 4. `数据库并发操作`:数据库插入、更新和删除操作多个事物同时修改同一条记录,而没有使用适当的锁机制或事务隔离级别。 5. `外部系统API接口重试`:对外提供的API接口可能由于调用方的重试逻辑,导致数据库操作被重复调用。 ## **如何解决这个问题** ### 唯一性约束 利用数据库的唯一性约束,如唯一索引或主键,来避免插入重复数据。 ```sql mysql> INSERT INTO `mydb`.`orders` (`order_id`, `user_id`, `product_id`, `quantity`, `order_status`, `create_time`, `pay_time`, `version`) VALUES ('ORD-20231023-0001', 'USR-A123456', 'PRD-X123', 2, 0, '2023-10-23 10:15:30', NULL, 1); ERROR 1062 (23000): Duplicate entry 'ORD-20231023-0001' for key 'orders.PRIMARY' ``` ### 乐观锁 通过记录数据的\`版本号\`或\`时间戳\`,仅当数据未被其他事务修改时,才允许更新操作执行。每次更新数据时,版本号都会递增。 ```sql UPDATE orders SET quantity = 1, order_status = 1, pay_time = '2024-04-30 10:20:00', version = version + 1 WHERE order_id = 'ORD-20231023-0001' AND version = 1; ``` ### 悲观锁 使用悲观锁,事务在读取数据时会锁定相应的数据行,直到事务结束(提交或回滚)。这可以防止其他事务在锁定期间修改这些数据,从而确保数据的一致性。 在执行读取操作时,使用 `SELECT ... FOR UPDATE` 语句来锁定相关记录。 ```sql - 锁定记录 SELECT * FROM orders WHERE order_id = 'ORD-20231023-0001' FOR UPDATE; -- 执行业务逻辑 UPDATE orders SET quantity = 1, order_status = 1, pay_time = '2023-10-23 10:20:00' WHERE order_id = 'ORD-20231025-0003'; ``` 悲观锁确保每个事务也能安全地执行,而不会导致数据不一致的问题。但是,悲观锁可能会因为锁定机制而导致 `性能问题` ,尤其是在高并发的系统中,这可能会引起 `锁争用和死锁` 。 ### 分布式锁 在分布式系统中,使用分布式锁来保证同一时间只有一个实例处理特定消息或请求。 ### 状态机 使用状态机是判断业务流程,确保操作只执行一次。 状态机设计: 1. 订单创建:订单初始化,状态为 `PENDING`(待支付)。 2. 支付操作:当订单状态为 `PENDING` 时,允许执行支付操作,支付成功后状态变为 `PAID`(已支付)。 3. 重复支付检查:如果再次尝试支付一个已经是 `PAID` 状态的订单,状态机将拒绝该操作,保持订单状态不变。 ### 全局请求唯一ID 调用接口时,生成一个唯一 id,redis 将数据保存到集合中(去重),存在即处理过。可以使用 nginx 设置每一个请求的唯一 id; ```xml proxy_set_header X-Request-Id $request_id; ``` Loading... > 古语云:“一而再,再而三,其效不二” > 俗语讲:被虐千百遍,依然如初恋 > 数学符号:f(f(f(x))) = f(x) > 即无论操作执行一次还是多次,其效果始终如一,不会有差异。这就是幂等性。 接口幂等性就是用户对于 `同一操作`发起的 `一次请求或者多次请求的结果是一致`的,不会因为多次点击而产生了副作用。 > 注意:数据库可能产生幂等性问题,但是幂等性问题不只发生在数据库。 比如在订单系统中,订单可以直接支付、积分抵扣、余额支付等不同的支付方式,除直接支付外,其他资产在获取的过程中可能存在以下过程:  由于网络存在不稳定的因素,这个通知可能会发送多次,极端请况下,同一笔订单的多次通知可能会同时到达商城服务端,如果不做幂等,那么同一笔订单就可能被多次处理,继而出现多次发放、ABA等一系列并发问题。 ## 如何产生幂等问题 1. `网络请求重试`:网络波动或超时,客户端可能会重复发送相同的请求。 2. `用户界面重复提交`:用户在用户界面上可能会不小心重复点击按钮,导致仙童的请求多次发送。 3. `消息队列重试机制`:使用消息队列时,消息可能会被重复消费。 4. `数据库并发操作`:数据库插入、更新和删除操作多个事物同时修改同一条记录,而没有使用适当的锁机制或事务隔离级别。 5. `外部系统API接口重试`:对外提供的API接口可能由于调用方的重试逻辑,导致数据库操作被重复调用。 ## **如何解决这个问题** ### 唯一性约束 利用数据库的唯一性约束,如唯一索引或主键,来避免插入重复数据。 ```sql mysql> INSERT INTO `mydb`.`orders` (`order_id`, `user_id`, `product_id`, `quantity`, `order_status`, `create_time`, `pay_time`, `version`) VALUES ('ORD-20231023-0001', 'USR-A123456', 'PRD-X123', 2, 0, '2023-10-23 10:15:30', NULL, 1); ERROR 1062 (23000): Duplicate entry 'ORD-20231023-0001' for key 'orders.PRIMARY' ``` ### 乐观锁 通过记录数据的\`版本号\`或\`时间戳\`,仅当数据未被其他事务修改时,才允许更新操作执行。每次更新数据时,版本号都会递增。 ```sql UPDATE orders SET quantity = 1, order_status = 1, pay_time = '2024-04-30 10:20:00', version = version + 1 WHERE order_id = 'ORD-20231023-0001' AND version = 1; ``` ### 悲观锁 使用悲观锁,事务在读取数据时会锁定相应的数据行,直到事务结束(提交或回滚)。这可以防止其他事务在锁定期间修改这些数据,从而确保数据的一致性。 在执行读取操作时,使用 `SELECT ... FOR UPDATE` 语句来锁定相关记录。 ```sql - 锁定记录 SELECT * FROM orders WHERE order_id = 'ORD-20231023-0001' FOR UPDATE; -- 执行业务逻辑 UPDATE orders SET quantity = 1, order_status = 1, pay_time = '2023-10-23 10:20:00' WHERE order_id = 'ORD-20231025-0003'; ``` 悲观锁确保每个事务也能安全地执行,而不会导致数据不一致的问题。但是,悲观锁可能会因为锁定机制而导致 `性能问题` ,尤其是在高并发的系统中,这可能会引起 `锁争用和死锁` 。 ### 分布式锁 在分布式系统中,使用分布式锁来保证同一时间只有一个实例处理特定消息或请求。 ### 状态机 使用状态机是判断业务流程,确保操作只执行一次。 状态机设计: 1. 订单创建:订单初始化,状态为 `PENDING`(待支付)。 2. 支付操作:当订单状态为 `PENDING` 时,允许执行支付操作,支付成功后状态变为 `PAID`(已支付)。 3. 重复支付检查:如果再次尝试支付一个已经是 `PAID` 状态的订单,状态机将拒绝该操作,保持订单状态不变。 ### 全局请求唯一ID 调用接口时,生成一个唯一 id,redis 将数据保存到集合中(去重),存在即处理过。可以使用 nginx 设置每一个请求的唯一 id; ```xml proxy_set_header X-Request-Id $request_id; ``` 最后修改:2025 年 12 月 19 日 © 允许规范转载 赞 如果觉得我的文章对你有用,请随意赞赏