幂等的理解和处理

什么是幂等

幂等(idempotent、idempotence)是一个数学与计算机学概念,常见于抽象代数中。

在编程中,幂等 的理解就是,在数据不变的情况下,一个操作,无论执行多少次,结果都是一样的

常用场景

  • 前端数据重复提交

  • 订单支付请求

    无论是网络超时?系统bug?还是其他原因,都只应该扣一次钱

  • Mpush推送消息

    推送多条同样的消息,用户会疯掉的

  • 发短信给用户

  • 用户下单

等等,很多熟悉的应用场景,都需要考虑幂等性,而导致数据不一致或者出问题的情况很多,

例如:重复点击? 重复提交?网络重发?或者nginx重发导致的数据重复提交等等

幂等技术方案

查询操作

查询一次和查询多次,在数据不变的情况下,查询结果是一样的。select是天然的幂等操作

删除操作

删除操作也是幂等的,删除一次和多次删除都是把数据删除。(注意可能返回结果不一样,删除的数据不存在,返回0,删除的数据多条,返回结果多个)

唯一索引

数据库创建唯一索引或者组合唯一索引,都可以避免脏数据,保证幂等,但是注意处理异常

token机制

之前在做接口开发的时候,思考过几个问题:

  1. 如何保证请求接口响应结果的唯一性
  2. 如何防止恶意请求或者攻击
  3. 分布式环境下,如何保证请求接口的响应结果

当时考虑的结果是用token + redis来实现的,流程如下:

  1. 请求之前,先问服务器要一个token,服务器缓存token到redis,加上超时时间
  2. 请求的时候,带着token
  3. 服务器从redis获取token校验,通过后,处理,响应,删除token,未通过校验,则提示token无效

注意

  1. token在redis的超时时间合理控制,防止恶意请求
  2. 服务器校验token的时候,采用redis.delete操作,直接使用get+delete 容易存在并发问题
  3. token在一定程度上可以限流

悲观锁

悲观锁,获取数据,用的不多,主要使用的不读,容易锁表。

select * from table_xxx where id='xxx' for update;

注意

  1. id字段一定是主键或者唯一索引,不然是锁表
  2. 悲观锁一般伴随事务一起使用,数据锁定时间可能会很长,根据实际情况选用

乐观锁

类似于CAS,也是compareAndSet操作,在更新数据那一刻锁表,其他时间不锁表,所以相对于悲观锁,效率更高,常用的就是加版本号控制,或者根据唯一条件。

update t_user set name='123',ver=ver+1 where ver='1.2.3' 

说起版本号,突然想起CAS的ABA问题------AtomicStampedReference的解决方案

分布式锁

​ 数据处理在分布式环境下一向是很很麻烦的,因为SynchronizeReentrantLock在分布式环境下是达不到并发锁的效果的。所以分布式锁,一般都采用第三方,例如,redis,zookeeper,就好比,去第三方认证获取一个permit,处理完了就去注销这个permit确保同一时刻,只有一个能执行成功

​ 分布式环境下,经常还需要考虑一个全局唯一索引的问题,常用解决方案如下:

  1. 雪花算法
  2. 令牌桶算法
  3. 漏桶算法
  4. 自定义唯一索引生成器

select + insert

在一些小型系统或者并发不高的项目里面,防止重复执行,可以采用select+insert

状态机

​ 如果状态机已经处于下一个状态,这时候来了一个上一个状态的变更,理论上是不能够变更的,这样的话,保证了有限状态机的幂等。

​ 在幂等的处理机制中,状态机是个很有用的东东,已经确定的状态不会回滚处理,比如订单业务里面,订单是已支付状态,再来这个订单的支付请求,肯定是不行的嘛,怼回去。

​ 很多场景或者系统中都用到状态机,所以设计合理的状态机制,是很有用且很必要的。

总结

​ 在接口开发,特别是和银行支付相关的项目中,幂等是首要考虑的问题,也是在面试中文的比较多的一个问题点,因此理解什么是幂等,如何保证幂等是很总要的。

参考 https://mp.weixin.qq.com/s/aqlWMxnneFhSIFJBPMW25A
本文大部分参考以上微信公众号的文章,如有侵权,请联系删除

### 等的定义及在编程中的应用 #### 等的定义 等(Idempotent)是一个数学领域与计算机科学中的概念,指一个操作或函数无论执行多少次,其结果始终相同。换句话说,对于一个等操作,多次执行与仅执行一次的效果完全一致[^2]。例如,在数学中,`f(n) = 1^n` 是等的,因为无论 `n` 的值为何,`f(n)` 的结果永远为 `1`[^4]。 在编程中,等性意味着对某个资源的操作(如数据库更新、API 请求等),即使重复调用,也不会改变其最终状态[^4]。例如,一个等的 HTTP 请求,无论发送多少次,服务器上的数据状态都不会发生变化。 --- #### 等在编程中的具体应用 1. **接口设计中的等性** 在微服务架构中,接口的等性是确保系统一致性的重要手段。例如,当用户通过 API 提交订单时,如果网络中断导致请求被重复发送,等的设计可以保证订单不会被重复创建[^2]。常见的等接口包括: - `GET` 请求:查询操作通常被认为是等的,因为它不会修改服务器状态。 - `PUT` 请求:更新资源时,指定完整的资源状态,多次调用不会产生副作用。 - `DELETE` 请求:删除资源后,再次调用不会改变结果。 2. **数据库操作中的等性** 数据库事务可以通过唯一标识符(如 UUID 或时间戳)来实现等性。例如,以下 SQL 语句通过条件判断确保了插入操作的等性: ```sql INSERT INTO orders (id, user_id, amount) SELECT 'unique-order-id', 123, 100 WHERE NOT EXISTS (SELECT 1 FROM orders WHERE id = 'unique-order-id'); ``` 3. **消息队列中的等性** 在分布式系统中,消息队列可能因网络问题导致消息重复传递。为了保证等性,消费者需要记录已处理的消息 ID。例如,使用 Redis 缓存来避免重复处理: ```python import redis r = redis.Redis() def process_message(message_id): if not r.exists(message_id): # 处理逻辑 print(f"Processing message {message_id}") r.set(message_id, "processed", ex=60) # 设置过期时间为 60 秒 else: print(f"Message {message_id} already processed") ``` 4. **函数式编程中的等性** 在函数式编程中,纯函数(Pure Function)天然具有等性,因为它们的输出仅依赖于输入,且没有副作用。例如: ```python def square(x): return x * x # 等操作,多次调用结果不变 ``` --- #### 等性的挑战与解决方案 尽管等性在理论上简单明了,但在实际应用中可能会遇到以下挑战: - **状态依赖**:某些操作的结果可能依赖于外部状态,难以保证等性。例如,银行转账操作涉及多个账户余额的变化。 - **性能开销**:为了实现等性,可能需要额外的检查逻辑(如唯一标识符验证),这会增加系统复杂性性能开销。 解决方案包括: - 使用唯一标识符(如 UUID)来标记每个操作。 - 引入缓存机制(如 Redis)以减少重复计算。 - 设计可重试的接口,并结合等控制策略。 --- ### 示例代码 以下是一个简单的 Python 示例,展示如何通过唯一标识符实现等性: ```python class IdempotentOperation: def __init__(self): self.cache = {} def execute(self, operation_id, operation_func): if operation_id not in self.cache: result = operation_func() self.cache[operation_id] = result return result else: return self.cache[operation_id] # 定义一个等操作 def increment_counter(): global counter counter += 1 return counter counter = 0 operation = IdempotentOperation() print(operation.execute("op1", increment_counter)) # 输出: 1 print(operation.execute("op1", increment_counter)) # 输出: 1(等性) ``` ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值