保证接口幂等性的多种实现方式(数据库方案)

本文详细阐述了幂等性在软件工程和Web服务中的重要性,包括其定义、核心价值以及在HTTP方法中的体现。讨论了幂等性在处理网络不稳定、用户误操作和恶意刷单等问题的应用场景,并介绍了数据库唯一索引、防重表、悲观锁和乐观锁等多种实现方式。
摘要由CSDN通过智能技术生成

1. 幂等性的概念

        接口幂等性是指在软件工程和Web服务领域中,一个接口(通常是HTTP API)无论被调用一次还是多次,其对系统产生的副作用应该是相同的,即结果保持一致,不会因为多次请求而有所不同。换句话说,多次执行同一操作应当产生与执行一次相同的效果,不会额外改变系统的状态。

2. 幂等性的核心价值

确保数据一致性:在存在网络延迟或不稳定的情况下,请求可能被重复发送。幂等性确保即使请求多次到达服务器,也不会导致多次创建资源、多次扣除账户余额等错误,从而维护了数据的一致性和完整性。

简化错误处理:客户端可以更简单地处理请求失败的情况,只需重试即可,无需复杂的逻辑来判断是否需要重试或如何处理潜在的副作用。

提升用户体验:用户操作(如点击支付按钮)因网络问题或误操作导致的重复提交,不会引起多次扣款等不良后果,增强了用户对系统的信任感。

3. 幂等性与HTTP方法

请求类型安全性与幂等性描述
GET安全且幂等用于获取资源,多次请求应返回相同的结果
PUT不安全但幂等用于替换整个资源。无论PUT操作执行多少次,资源最终状态都是一样的,即被最后的请求体内容所替换
POST不安全且不幂等用于创建新的资源,多次调用可能会创建多个资源
DELETE不安全但幂等重复提交,删除一个已经不存在的资源,不会改变系统状态

        其中POST是非幂等的,因为它用于创建新的资源,多次调用可能会创建多个不同的资源。但在某些设计中,如果POST请求设计为总是创建相同的结果(基于请求数据和当前系统状态),也可以实现幂等性。

4. 幂等性的应用场景

        幂等性对于保证系统的一致性和可靠性至关重要,尤其是在分布式系统和网络不稳定环境下,它有助于处理如下几种常见场景:

  • 网络重传:在网络不稳定的情况下,请求可能会被重传,幂等性确保重传不会导致多次执行操作,如多次扣款。
  • 用户误操作:用户可能因页面刷新、误点击等原因重复提交请求,幂等性保护用户免受因此导致的数据不一致影响。
  • 用户恶意刷单:比如在抢兑换码、投票这种互动中,可能会有用户会进行恶意的重复发送请求,恶意刷单,使结果不实。
  • 异步消息重复处理:在使用消息队列或事件驱动架构时,消息可能因故障恢复机制被重新投递,幂等性处理可以避免业务逻辑被重复执行

5. 解决方案

保证接口幂等性有多种方法,当前提供数据库方案,如下:

5.1. 数据库唯一索引

这可以算是最简单的方法了,直接在数据库表中的某个关键字段设置唯一索引,下面以订单(order)数据库表为例写一段SQL:

alter table `order` add UNIQUE KEY `t_xxx` (`某个字段`); 

加了唯一索引之后,第一次请求数据可以插入成功。但后面的相同请求,插入数据时会报异常,表示唯一索引有冲突:

Duplicate entry '002' for key 'order.t_code

具体流程图如下:

5.2. 建立防重表

        有同学可能就问了,上面的方案不太合理,因为有时候我们的表中需要一些重复数据,只有一些特殊场景才不需要重复数据,唯一索引方案可能就不合适了。

        这个时候可以试试防重表,简单来说就是我们再单独建立一张表,只需要含有id和想要唯一的索引字段。

具体流程图如下:

5.3. 悲观锁机制

        悲观锁机制可以作为一种手段来帮助解决接口幂等性问题,尤其是在处理高并发场景下对数据的并发修改需求。悲观锁的基本假设是数据在并发访问期间很可能发生冲突,因此在事务开始时就立即锁定所需的数据资源,防止其他事务对其进行修改,直到当前事务结束。

        以下是利用MySQL悲观锁机制解决接口幂等性的基本方案:

1. 使用SELECT ... FOR UPDATE

        在执行更新操作之前,先使用SELECT ... FOR UPDATE语句锁定要修改的行。这会在读取数据的同时加上排他锁(X锁),确保其他事务无法修改这些数据,直到当前事务结束(提交或回滚)。

START TRANSACTION;

SELECT * FROM your_table WHERE id = ? FOR UPDATE;

-- 根据查询结果判断是否需要更新
IF (需要更新) THEN
    UPDATE your_table SET column=value WHERE id = ?;
END IF;

COMMIT;

2. 结合接口幂等性设计

结合上述SQL操作,可以设计一个幂等性接口处理流程:

        大缺点:悲观锁需要在同一个事务操作过程中锁住一行数据,如果事务耗时比较长,会造成大量的请求等待,影响接口性能。

5.4. 乐观锁机制

        悲观锁会增加锁争用的可能性,可能导致事务等待和阻塞,影响系统性能,特别是在高并发场景下。因此,应谨慎使用,乐观锁某些情况下就是个很好的替代方案。

        MySQL乐观锁机制是一种非阻塞的并发控制策略,它假定多线程同时修改数据的概率较低,因此不会在事务一开始就对数据加锁,而是在更新数据时检查数据是否被其他事务修改过。这对于高并发场景下解决接口幂等性问题特别有效,因为它减少了锁的竞争,提高了系统的吞吐量。以下是利用MySQL乐观锁机制解决接口幂等性的方案:

1. 版本字段实现乐观锁

在数据库表中添加一个额外的字段作为版本控制,比如version字段,初始值为1。

CREATE TABLE your_table (
    id INT PRIMARY KEY,
    data VARCHAR(100),
    version INT DEFAULT 1
);

2. 接口调用流程

客户端请求:客户端发出修改数据的请求,请求中包含数据的原始版本号(如果有的话)。

读取数据并附带版本信息:服务端首先查询需要更新的数据,并获取其当前的版本号。

SELECT * FROM your_table WHERE id = ?;

 幂等性检查与更新:在更新数据时,同时在UPDATE语句中加入版本号的条件检查,确保只有当版本号与查询时一致时才执行更新。

   UPDATE your_table 
   SET data = ?, version = version + 1 
   WHERE id = ? AND version = ?;

---(data = ? 是要更新的新数据值。
---version = version + 1 表示更新成功后,版本号递增。
---WHERE id = ? AND version = ? 确保只有当记录的版本号与预期一致时才执行更新。)

④ 结果判断:根据UPDATE语句影响的行数判断是否更新成功。

如果影响行数为1,说明更新成功,且数据没有被其他事务修改过。
如果影响行数为0,说明数据在执行更新前已经被其他事务修改,此时可以根据业务逻辑选择重试或返回错误信息。

具体流程图如下:

今天写到这里,明天再来写token方案哈。

  • 34
    点赞
  • 29
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
幂等性指的是对同一接口多次调用,结果是一样的,不会产生副作用。在实际场景中,比如网络不稳定、请求超时等情况下,可能会导致接口被重复调用,如果接口不具备幂等性,就有可能造成数据重复提交等问题。 为了保证接口幂等性,我们可以在接口中添加某些操作,比如在数据库中添加唯一约束、使用分布式锁等等。下面是一个使用 Spring Boot 和 Redis 实现接口幂等性的示例代码: ```java @RestController public class DemoController { @Autowired private RedisTemplate<String, String> redisTemplate; @PostMapping("/demo") public String demo(@RequestParam("id") String id) { String key = "demo:" + id; ValueOperations<String, String> opsForValue = redisTemplate.opsForValue(); Boolean absent = opsForValue.setIfAbsent(key, "true"); if (absent != null && absent) { // 执行业务逻辑 // ... redisTemplate.delete(key); return "success"; } return "fail"; } } ``` 在这个示例中,我们使用 Redis 来实现接口幂等性。当第一次请求接口时,我们使用 Redis 的 setIfAbsent 方法来设置一个键值对,如果设置成功,说明这个接口还没有被调用过,可以执行业务逻辑。执行完业务逻辑之后,我们再删除这个键值对,这样下次再请求同一接口时,就不会重复执行业务逻辑了。 需要注意的是,接口幂等性实现不是一成不变的,具体实现方式需要根据业务场景进行调整和优化。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

stu_kk

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值