高并发下如何保证接口的幂等性?

我是阿沐
• 阅读 1828

1 前言

大家好,我是阿沐!”幂等“这个词语或许小伙伴很少见,基本上中小型公司或者一些大公司都未使用过,但是并不代表小伙伴们没有接触到。

为啥我会扯到这个技术话题?缘由就是20年我面试了一些大厂包括身边朋友的面试经历,例如腾讯、网易、字节等等大厂,其中大都会遇到”幂等的概念、理解以及实现与应用“,那么下面就听我一一道来幂等的相关知识。

2 什么是幂等性?

数学中:在一次元运算为幂等时,其作用在任一元素两次后会和其作用一次的结果相同;在二次元运算为幂等时,自己重复运算的结果等于它自己的元素。

计算机学中:幂等指多次操作产生的影响只会跟一次执行的结果相同,通俗的说:某个行为重复的执行,最终获取的结果是相同的,不会因为重复执行对系统造成变化。

3 为什么要使用幂等性?

众所周知,目前随着js的发展web端慢慢转变了前后端分离,通过接口实现;小程序、app同样都是api实现,基本上接口都是正常的返回信息,并未涉及到重复提交或者是并发提交的情况。但假如我们考虑的细致一点,比如电商系统,抽奖活动、用户反馈、订单支付、消息消费、商品评价、商品点赞等这些都是和幂等息息相关。举几个例子给小伙伴们看下:

  • ① 用户重复下订单:当用户下单时,因为网络问题或者手速过快,导致重复下单。

  • ② 消息重复消费:当使用MQ消息中间件时候,如果消息中间件发生异常出现错误未及时提交消费信息,导致消息被重复消费。

  • 抽奖活动(券):当用户参加抽奖活动需要消耗抽奖券时,如果出现并发请求导致抽奖券余额更新错误。

  • 重复提交表单:当用户填写表单提交时,可能会因为用户点多次连击提交或者网络波动导致服务端未及时响应,会导致用户重复的提交表单,就出现了同一个表单多次请求。

这些只是我们常见的一些状况,还需要根据自己的项目的实际情况进行分析,判断是否需要幂等操作,举个简单例子:运营做了一次大型活动,参与人数10w+(每人只能给一个用户点赞冲榜),活动结束后运营需要复盘,这个时候发现一些用户给一个人点赞又多次状况。这个时候,嘿嘿嘿...... 开发过来背锅喽!

4 我们如何在业务功能上实现幂等性?

通常数据库实现主要是利用数据库表中主键唯一约束+唯一索引的特性,如果主键唯一或者设置了复合唯一索引,在”插入“数据的时候就是幂等性操作。例如还有悲观锁、乐观锁、redis锁、token令牌、依赖前后端配合生成请求序列号。ps:基本上面试时,大厂都会被问到的问题,不要问我为什么?因为面试官喜欢问。

高并发下如何保证接口的幂等性?

下面是设计一个活动抽奖大转盘用户抽奖券余额:抽奖券来源参加活动获取,抽奖消耗券场景:

CREATE TABLE `mumu_test` (
  `id` int(11) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键自增id',
  `userid` bigint(20) unsigned NOT NULL DEFAULT '0' COMMENT '用户id',
  `act_id` varchar(125) COLLATE utf8mb4_bin NOT NULL DEFAULT '' COMMENT '活动ID',
  `lottery` bigint(20) unsigned NOT NULL DEFAULT '0' COMMENT '余额',
  PRIMARY KEY (`id`),
  UNIQUE KEY `uniq_uid_aid` (`userid`,`act_id`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='沐沐测试春节抽奖券记录表';

唯一主键索引实现幂等性

通常情况下,我们在做这种用户活动抽奖券记录数据时,会先select下看看是否已经有插入的记录了,如果已存在则update,否则insert。那么我现在先说说不存在添加数据的情况:

存在用户在做活动任务时,因为网络抖动导致服务端响应超时,这个时候用户以为并没领取奖券成功,就会疯狂的点击领取按钮,那么就会导致同一个任务奖券出现多次请求,那么我们第一次insert添加肯定成功了,当并发请求过来时就会重复执行以下sql语句:

inser into mumu_test('userid','act_id','lottery')values(123,'spring',1)

由于存在userid+act_id唯一键,那么就会出现只有一条数据插入成功,其他的数据就会插入失败,保证了数据的幂等。推荐使用

乐观锁实现幂等性

通俗地讲:它的心态就是很乐观,每次去拿数据的时候都认为别人不会修改,所以不会上锁,但是在提交更新的时候会判断一下在此期间别人有没有去更新这个数据。它就像单纯的孩子一样,总是认为不会产生并发冲突的场景,我只是在你提交操作时检查是否违反数据完整性。所以乐观锁适用于读多写少的应用场景,这样可以提高吞吐量。

划重点:一般使用版本号控制version,即为数据增加一个版本标识,一般是通过为数据库表行数据增加一个数字类型的“version”字段来实现。当读取数据时,会将version字段的值一同读出,数据每更新一次,对此version值加1操作。当我们提交更新的时候,判断数据库表对应记录的当前版本信息与第一次取出来的version值进行比对,如果数据库表当前版本号与第一次取出来的version值相等,则予以更新,否则认为是非法操作。

场景应用:针对上面的表我们增加一个版本标识:

alter table mumu_test add `version` smallint(5) unsigned not null default '0' comment '版本号'

添加成功之后,更新操作:

1.获取抽奖券:
update mumu_test set lottery = lottery + 5, version = version + 1 where id = 123 and version = 1;

2.消耗抽奖券
update mumu_test set lottery = lottery - 1, version = version + 1 where id = 123 and version = 2;

实现原理:更新数据的同时version+1,然后判断本次update操作的影响行数,如果大于0,则说明本次更新成功,如果等于0,则说明本次更新没有让数据变更。当并发请求过来时,只需要拿到select的版本号,进行更新操作即可(where可带上主键id),保证幂等。推荐使用

悲观锁实现幂等性

顾名思义,悲观锁它是一种悲观的心里状态,对应于生活中悲观的人总是想着事情往坏的方向发展。它像是一个彻底地loser,它认为别人每次去拿数据都会修改这条数据,所以每次拿数据的时候,都会使数据处于锁定状态。执行下面sql语句锁住该条记录:

select * from mumu_test where userid = 123 and act_id = 'spring' for update;

大家可以看到我并没有使用主键id是查询,首先我们并不知道这条记录id值,所以我们通过uid+aid组合的唯一建作为锁表行记录条件,一定要使用主键或者唯一建,不然会将整张表都被锁住,那么其他的用户就无法操作了。

因为悲观锁是需要在同一个事物操作过程中锁住一行数据,假如我们事务逻辑耗时比较久,就会导致后面请求的堆积,直接影响到了整体响应时长。不推荐使用

Token令牌如何实现幂等性

所谓的token令牌其实就是为了防止用户重复提交一个表单信息,这一点基本上PHP的框架都会带有token验证。服务端需要生成一个全局唯一的id,(例如:snowflake雪花算法美团Leaf算法滴滴TinyID算法百度Uidgenerator算法uuidredis等)。

  1. 客户端每次进入表单页面可以优先申请一个唯一令牌存储本地,服务端存储令牌token值(redis,文件,memcache都可)

  2. 每次发送请求时可以在Headers头部中带上当前这个token令牌

  3. 服务端验证token是否存在,存在则删除token,执行后续业务逻辑;不存在则响应客户端重复提交提示语

生成全局唯一id的代码,大家可以网上自行搜索,基本上是千篇一律的,放心抄过来使用就可以了。

最后总结

幂等性基本上在中大型公司项目需求中都能遇到,尤其是现在消息中间件(kafka、rabbitmq等)的广泛使用,更加注重消息的幂等。那么像我之前在电商公司,支付订单、抽奖券、部分活动相关的中台服务对接口的幂等性都是很重要的,所以我们在日常开发中,可以针对不同的业务场景选择合适的幂等方案,即可满足要求同时也减少性能影响,更重要的是不会因为出bug被产品运营diss。开发真的好卑微啊~

聊幂等性这个词语,也是自己想了很久。在这之前我推荐不少开发(经验基本上5年+)到大厂,他们给的反馈就有幂等这个概念的询问。ps:当然并不说明他们能力不行、技术差,幂等名词一般大家很少去注意,尤其是常年待在一个公司,并不一定能接触到幂等业务,就算接触到了类似场景;而都是认为对于一致性的要求不能重复插入数据,并不会想起是幂等这个名词。所以并不奇怪,大家也不要在面试中遇到新的名词就内心慌乱,手心出汗、腿发抖、发冷汗,我们完全可以跟面试官聊,是否可以换一种方式来问这个问题;我相信大部分的面试官都能接受,顶多就认为你知识量不够广,不知道这些专业术语等等,不会太影响你后面的解决思路。

宫崎骏曾说过一句话:“要努力做一个可爱的人,不埋怨谁,不嘲笑谁,也不羡慕谁,阳光下灿烂,风雨中奔跑,做自己的梦,走自己的路。”

我是阿沐,一个不想30岁就被淘汰的打工人 ⛽️ ⛽️ ⛽️ 。

点赞
收藏
评论区
推荐文章
3A网络 3A网络
1年前
Redis 做接口限流
Redis除了做缓存,还能干很多很多事情:分布式锁、限流、处理请求接口幂等性。。。太多太多了~今天想和小伙伴们聊聊用Redis处理接口限流,这也是最近的TienChin项目涉及到这个知识点了,我就拎出来和大家聊聊这个话题。1.准备工作首先我们创建一个SpringBoot工程,引入Web和Redis依赖,同时考虑到接口限流一般是通过
peter peter
3年前
Go:分布式锁实现原理与最佳实践
分布式锁应用场景很多应用场景是需要系统保证幂等性的(如api服务或消息消费者),并发情况下或消息重复很容易造成系统重入,那么分布式锁是保障幂等的一个重要手段。另一方面,很多抢单场景或者叫交易撮合场景,如dd司机抢单或唯一商品抢拍等都需要用一把“全局锁”来解决并发造成的问题。在防止并发情况下造成库存超卖的场景,也常用分布式锁来解决。实现
Wesley13 Wesley13
2年前
HTTP 的幂等性
本文主要以HTTPGET、DELETE、PUT、POST四种方法为主进行语义和幂等性的介绍。一、HTTP的幂等性HTTPGET方法用于获取资源,不应有副作用,所以是幂等的。比如:GEThttp://www.bank.com/account/123456,不会改变资源的状态,不论调用一次还是N次都没有副作用。请注意,
Stella981 Stella981
2年前
Spring Boot 接口幂等插件使用
幂等概述幂等性原本是数学上的概念,即使公式:f(x)f(f(x))能够成立的数学性质。用在编程领域,则意为对同一个系统,使用同样的条件,一次请求和重复的多次请求对系统资源的影响是一致的。幂等性是分布式系统设计中十分重要的概念,具有这一性质的接口在设计时总是秉持这样的一种理念:调用接口发生异常并且重复尝试时,总
Stella981 Stella981
2年前
Kafka设计
1.幂等消息为了解决重试导致的消息重复、乱序问题,kafka引入了幂等消息。幂等消息保证producer在一次会话内写入一个partition内的消息具有幂等性,可以通过重试来确保消息发布的ExactlyOnce语义。实现逻辑很简单:区分producer会话producer每次启动后,首先向broker申请一
Stella981 Stella981
2年前
Kafka Exactly
!(https://oscimg.oschina.net/oscnet/0001a9db0c7346a6b32db4586329cae6.png)作者|王蒙整理|无风我起浪这篇文章主要讲述Kafka事务性的实现,这部分的实现要比幂等性的实现复杂一些,幂等性实现是事务性实现的基础,幂等性提供了单会话单Partiti
Wesley13 Wesley13
2年前
MQ实现消息的幂等性
一、什么是幂等性可以参考数据库乐观锁机制,比如执行一条更新库存的SQL语句,在并发场景,为了性能和数据可靠性,会在更新时加上查询时的版本,并且更新这个版本信息。可能你要对一个事情进行操作,这个操作可能会执行成百上千次,但是操作结果都是相同的,这就是幂等性。!(https://img2018.cnblogs.com/blog/1841773/201
Stella981 Stella981
2年前
Redis如何保证接口的幂等性?
在最近的一次业务升级中,遇到这样一个问题,我们设计了新的账户体系,需要在用户将应用升级之后将原来账户的数据手动的同步过来,就是需要用户自己去触发同步按钮进行同步,因为有些数据是用户存在自己本地的。那么在这个过程中就存在一个问题,要是因为网络的问题,用户重复点击了这个按钮怎么办?就算我们在客户端做了一些处理,在同步的过程中,不能再次点击,但是经过我最近
Stella981 Stella981
2年前
SpringBoot接口幂等性实现的4种方案!
!(https://oscimg.oschina.net/oscnet/42a233a0deb143899955ec0d6d7805c6.jpg)作者|超级小豆丁来源|www.mydlq.club/article/94目录什么是幂等性什么是接口幂等性为什么需要实现幂等性
Easter79 Easter79
2年前
SpringBoot接口幂等性实现的4种方案!
!(https://oscimg.oschina.net/oscnet/42a233a0deb143899955ec0d6d7805c6.jpg)作者|超级小豆丁来源|www.mydlq.club/article/94目录什么是幂等性什么是接口幂等性为什么需要实现幂等性
我是阿沐
我是阿沐
Lv1
男 · 腾讯音乐后端开发工程师 | 微信搜:我是阿沐
思绪来得快去得也快,偶尔会在这里停留
文章
15
粉丝
3
获赞
5