【互联网常见问题】-幂等性

目录

什么是幂等性

概述

幂等产生场景有哪些

用户界面:

浏览器操作:

网络层重试:

系统:

消息队列:

接口幂等

前端防重

PRG模式

Token机制

服务幂等

防重表的实现策略

悲观锁的局限性

MySQL乐观锁的应用场景

举例

SQL举例

1. 查询语句:

2. 插入语句:

3. 更新语句(固定值):

4. 更新语句(基于当前值的计算):

5. 删除语句:

参考资料


什么是幂等性

概述

  • 幂等性是分布式系统设计的关键概念,特别是在支付和订单处理等关键业务场景中。
  • 一些编程操作如SQL的SELECT、DELETE和UPDATE在特定条件下是天然幂等的。
  • 在编程中,幂等性意味着多次对同一资源操作应产生相同的影响,但操作的返回值可以不同。
  • 它确保一个操作无论执行多少次,都会产生相同的结果。
  • 幂等性起源于数学,表示一个操作多次执行结果不变,如 f(n) = 1^n 总是等于1。
  • 幂等性是分布式系统常见的挑战,也是技术面试的常见问题。
  • 分布式或微服务架构中的系统由多个微服务组成,这些服务需要相互通信和调用。
  • 服务调用可能遇到延迟或失败,可能导致服务端重试或用户多次提交相同请求。
  • 实现业务操作的幂等性可以确保数据处理结果的一致性,防止重复操作导致结果不一致。
  • 幂等性关注操作对资源的影响而非操作的最终结果。

幂等产生场景有哪些

幂等性问题通常由客户端行为或网络通信中的重试机制引起,这可能导致同一操作被多次执行。以下是一些常见的情况:

用户界面:

用户可能会不小心多次点击提交按钮(手抖或者暂时没有相应误以为没点到),

页面没有响应而导致多次下单或者交易

在操作过程中点击刷新按钮,

这都可能导致操作被重复执行。

浏览器操作:

用户在浏览器中后退并重新执行之前的操作,可能会无意中再次提交表单。

网络层重试:

例如,Nginx等代理服务器或负载均衡器可能因为网络超时或错误而自动重试请求。

系统:

在分布式RPC(远程过程调用)环境中,由于网络问题或服务暂时不可用,客户端可能会尝试重新发送同一请求。

超时或者失败重试机制。

中间件、应用服务根据自身特性,也有可能进行重试。

消息队列:

在使用消息队列(如MQ)的系统中,如果消息确认过程中出现错误,可能会导致消息被重复消费。

为了处理这些情况,系统设计应该考虑到操作的幂等性,确保重复执行同一操作不会导致不一致的状态或数据。这可以通过各种技术实现,比如使用唯一事务ID来检测和阻止重复操作,或者设计操作逻辑以确保自然幂等性,例如,通过检查记录是否存在来决定是否插入记录,或者使用覆盖式更新而不是基于当前状态的更新。

接口幂等

在实现接口幂等性的方案中,以下是针对前后端交互和服务间交互的具体解决方法:

前端防重

   - 这是通过前端技术实现幂等性的最简单方法。可以通过禁用提交按钮或使用JavaScript代码来防止用户重复提交表单。
   - 尽管这种方法实施简单,但它并不十分可靠,因为熟练的用户可能会使用工具绕过这些限制。
   - 这种方法主要适用于防止表单重复提交或按钮重复点击的场景。

PRG模式

   - PRG即POST-REDIRECT-GET模式。在这种模式下,用户提交表单后,服务器会重定向用户到一个新的页面,而不是停留在原表单页面。
   - 这样可以防止用户刷新页面导致的表单重复提交,并且可以避免使用浏览器的前进/后退按钮导致的重复提交。
   - PRG是一种流行的前端防重策略。

Token机制

   - Token机制是一种常见的解决方案,适用于多数场景,需要前后端交互配合。
   - 服务端提供一个获取Token的接口,客户端在发送请求前先获取这个Token。
   - 对于分布式系统,服务端生成的Token通常存储在Redis这样的分布式缓存中;对于单体架构,Token可以存储在JVM缓存中。
   - 客户端在请求时携带Token,服务端接收到请求后,会检查Token是否存在于Redis中。如果Token存在,服务端进行业务处理,并在处理完成后删除Token。如果Token不存在,表明请求是重复的,服务端会直接返回一个错误或特定标识给客户端。

这些方案可以根据具体的业务需求和系统架构进行选择和调整。例如,前端防重和PRG模式更多地涉及客户端的控制,而Token机制则需要前后端的紧密协作。在实现幂等性时,通常需要综合考虑用户体验、系统性能和安全性等因素。

服务幂等

防重表的实现策略

为了防止数据的重复提交,我们可以采用防重表的策略。这个方法的基本步骤如下:

  1. 创建一张专用的防重表,并在该表中建立一个或多个字段的唯一索引,这些字段作为防重标识,确保即使在并发的情况下也只能插入唯一的数据记录。
  2. 在业务数据插入主表之前,先尝试向防重表插入数据。如果插入防重表失败(通常由于唯一索引冲突),则认定为重复数据,拒绝进一步处理。

悲观锁的局限性

尽管悲观锁可以防止数据并发问题,但它存在死锁的风险。悲观锁通常通过锁定数据行来实现,但在高并发环境下,可能会出现两个或多个进程互相等待对方释放锁的情况,从而导致死锁。

例如,用户A锁定了表A并尝试访问表B,而用户B锁定了表B并尝试访问表A。如果两者都在等待对方释放锁,那么系统就会陷入死锁状态。

MySQL乐观锁的应用场景

MySQL的乐观锁是一种基于行锁概念的分布式锁实现方式,主要有两种形式:

  1. 基于版本号:

    • 这种方法通过给数据记录添加一个版本号字段来实现。在更新时检查版本号是否一致,如果一致则执行更新并增加版本号,否则更新失败。
    • 这种方式适用于大多数场景,但在如库存扣减的场景中可能会导致只有一个用户能成功购买,尽管这避免了超卖的问题。
  2. 基于条件更新:

    • 另一种乐观锁的实现是基于条件更新,例如,只有当库存数量足够时,才允许扣减操作。
    • 这种方法适用于需要避免超卖的场景,确保库存的正确性。

乐观锁在竞争不激烈、冲突概率较低的场景中效果较好。但是,乐观锁的操作会直接作用于数据库,可能会对数据库性能造成一定影响。此外,由于MySQL的连接数量有限,大量的锁操作可能会占用过多连接,从而成为数据库性能的瓶颈。因此,在高并发场景下,应谨慎使用乐观锁,以避免对数据库性能造成不利影响。

举例

SQL举例

1. 查询语句:

SELECT * FROM table WHERE id = 1;

这个查询操作是幂等的,因为它不会修改任何数据。每次执行都会返回具有相同 `id` 值的记录,不改变表的状态。即使返回的结果在并发环境中因其他事务的影响而不同,查询操作本身并未改变数据。

2. 插入语句:

INSERT INTO table(id, name) VALUES(1, 'heima');

这个插入操作的幂等性取决于表的约束定义。如果 `id` 或 `name` 列有唯一性约束(例如,`id` 是主键或具有唯一索引),则尝试插入相同的 `id` 和 `name` 值将导致违反唯一性约束的错误。因此,实际上只有首次插入会成功,后续插入会失败,这种情况下操作是幂等的。如果没有这样的约束,每次执行都会插入新记录,这不是幂等的。

3. 更新语句(固定值):

UPDATE table SET score = 100 WHERE id = 1;

这个更新操作是幂等的,因为无论执行多少次,它都会将具有 `id` 值为1的记录的 `score` 字段设置为100。每次执行之后的状态相同,不会因重复执行而改变。

4. 更新语句(基于当前值的计算):

UPDATE table SET score = score + 50 WHERE id = 1;

这个更新操作不是幂等的,因为它基于当前 `score` 值进行计算。每次执行都会将 `score` 增加50,因此多次执行会导致不同的结果。

5. 删除语句:

DELETE FROM table WHERE id = 1;

这个删除操作是幂等的,因为一旦执行,具有 `id` 值为1的记录将被删除。之后的同样操作将不会找到任何记录来删除,因此表的状态不会改变。无论执行多少次,结果都是表中不再有 `id` 值为1的记录。

参考资料

https://blog.csdn.net/HRX98/article/details/123338638
https://blog.csdn.net/Huangjiazhen711/article/details/127585296
https://blog.csdn.net/baidu_38493460/article/details/132619338
https://blog.csdn.net/weixin_50966947/article/details/128967722

有用请点赞,养成良好习惯!

疑问、交流、鼓励请留言!

  • 0
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

慕白Lee

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

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

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

打赏作者

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

抵扣说明:

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

余额充值