关于java接口幂等性和防重请求解决方案

概念

接口幂等性是指对同一操作的一次请求或多次请求返回的结果是一致的,不会因为多次请求就产生不一样的结果,比如数据库的select操作,可以看成是幂等性的,而插入和更新操作,则要保证重复提交造成的数据重复或者数据不正确的问题。

比如新增操作,假设名称不能重复,那么往往就要保证在新增过程中使数据库不要出现同名的数据,这是典型的防重操作。

比如更新操作,假设每次更新数值加一,那么就要保证在新增过程中不会因为重复请求而导致数值增加错乱,要保证幂等性,如果只是update 表 set is_enable = 0或1这种固定的值,就没关系,这种操作是幂等性的。

防重,顾名思义,防止数据库产生重复数据,对返回的结果是没什么要求的,所以如果是幂等性处理,在数据防重的基础上,还要把重复提交的请求返回的结果和第一次提交的请求返回的结果保持一致,比如统一返回操作成功的提示,但只有第一次是真正执行了。

适用场景

1.前端如果忘了做遮罩层,快速点击多次新增的情况下,数据库极易出现重复数据;
2.微服务间调用接口超时的时候,如果配置了重试机制,也很容易出现重复请求的情况;
3.在使用rocketMq消息队列在消费消息的时候,有时也会同时有重复消费的情况,比如下面两种情况:
① 消息生产者没有收到消息队列收到消息的应答,重试机制使得重复产生消息。
比如网络故障导致应答消息丢失或者消息太多 ,应答消息传回受到阻塞,生产者等待超时。
②消息已经到达消息队列,但发送给消费者的时候,没有收到来自消费者的回复消息,或者消息中间件更改消息状态出现问题。

常用的解决方案

数据新增的时候通常先从数据库select确认数据不存在后再插入,但是这样依然无法避免多次连击的情况(实测,只要够快),所以思路大体分成两个方向,一个是用数据库方式解决,一个是非数据库的方式。

数据库方向可分为:悲观锁、乐观锁、加唯一索引、建防重表(本质上还是唯一索引),加状态字段,更新完改变状态(本质上和乐观锁很像)

非数据库方向可分为:借用第三方缓存解决,比如redis或者结合前端用token.

先说一下我觉得这里面最适合的方式,用数据库解决会加重数据库的性能负担,而token的方案,要请求两次,而且要前端配合(既然都要前端配合了,不如直接让前端加个遮罩层来的快速方便)所以优先考虑用redis做分布式锁的方式,下面也会稍微介绍下每个方式的原理。

redis分布式锁

比如新增用户的接口,请求开始时,先把不能重复的字段比如用户名放到redis缓存里,可以用set或者setnx都行,设置比如两秒的过期时间(可根据业务自行设置),然后再select数据库里有没这个用户,如果没有则插入,插入成功之后删除redis里的缓存,返回操作成功。此时,如果有其他重复的请求进来,redis里已经有缓存的情况下,则直接返回操作成功就好了,或者返回数据已存在的提示,但是不会再往下执行。

这种方法应该是最简单的,而且效率最高的方法了。原理和数据库的分布式锁差不多,只是放在了redis里不会影响数据库的性能。但是关键最后一定要删除redis里的缓存。

token方案

需要让前端先请求后台生成token的接口,然后前端请求的时候header里带上这个token,后台拿到token之后放入redis里,设置缓存过期时间,再去数据库select看数据存不存在,不存在则新增,成功之后删除redis里的token,这种方案,要求前端那边请求两次,一次拿token,一次业务请求带上token,而且快速点击时,多次请求带的都是同一个token, 这个原理上还是分布式锁,只是可以全局配置,并不关心具体是什么字段,但是要请求方在请求之前先拿到唯一的token,如果连拿token这个请求都是重复的话就不太容易判断了。

数据库悲观锁

利用select for update锁住数据库中的一行数据,更新完之后别的请求才能更新这条数据,比如:

select * from user where id=1 for update;

这样再更新这一行的数值做加减运算的时候就不怕用其他并发请求过来引发数据错乱问题了,比如:

update user set like_num = like_num+1 where id = 1;

但是这种需要注意,如果是mysql,存储引擎必须用支持事物的innodb,这里id字段一定要是主键或者唯一索引,不然会锁住整张表。

数据库乐观锁

给要操作的行加个版本字段,每次更新前先查出这一行数据的版本,更新的时候指定这一行数据和版本,并且版本id+1,比如:

update user set like_num = like_num+1,version = version+1
where id = 1 and version = 1;

这样即便有重复请求,但是更新之后version变成了2,那么再更新version=1的必然不会生效。

以上两种数据库操作并不会解决新增数据重复的问题,能解决更新数据数值计算错乱的问题。

加唯一索引

这个基本是数据库版的分布式锁,有效解决数据重复问题,当插入重复字段数据时,会抛异常,不会插入成功。

建防重表

这种是把上面的唯一索引单独建一张表,这样就能在需要防重的时候先插入防重表来防重,不需要防重的时候又能存储这个字段的重复数据,更加灵活一点,这个思路把这个防重表放到redis里就是redis分布式锁了,其实思路差不多,只是一个在数据库,一个借助第三方缓存。

加状态字段

比如活动待审核-审核中-已审核-执行-结束-删除等流程操作的时候,每次更新都会造成状态的改变,这个就像是上面加了版本字段一个原理,更新的时候指定状态,更新完之后状态也改变了,这样重复的请求过来就不会更新成功,比如:

update activity set status = 2 where status = 1 and id = 1;

总结

综上,感觉redis缓存分布式锁的方式是效率最高的,推荐这个。
不同业务需求可以结合使用,会更香。

比如转账场景,先把订单id放入redis缓存做分布式锁,然后用数据库乐观锁加个版本字段控制,会更加保险一点。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 214,658评论 6 496
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,482评论 3 389
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 160,213评论 0 350
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,395评论 1 288
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,487评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,523评论 1 293
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,525评论 3 414
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,300评论 0 270
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,753评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,048评论 2 330
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,223评论 1 343
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,905评论 5 338
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,541评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,168评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,417评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,094评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,088评论 2 352

推荐阅读更多精彩内容

  • 1,消息队列使高并发系统中常见的一种组件,他可以将消息生产方和消费方解耦,减少突发流量对于系统的冲击。2,在编程中...
    滔滔逐浪阅读 2,113评论 0 0
  • 一、幂等性概念 在编程中.一个幂等操作的特点是其任意多次执行所产生的影响均与一次执行的影响相同。幂等函数,或幂等方...
    匆匆岁月阅读 1,137评论 0 31
  • 高并发下接口幂等性解决方案 一、幂等性概念在编程中.一个幂等操作的特点是其任意多次执行所产生的影响均与一次执行的影...
    ongahong阅读 600评论 0 2
  • 表情是什么,我认为表情就是表现出来的情绪。表情可以传达很多信息。高兴了当然就笑了,难过就哭了。两者是相互影响密不可...
    Persistenc_6aea阅读 124,726评论 2 7
  • 16宿命:用概率思维提高你的胜算 以前的我是风险厌恶者,不喜欢去冒险,但是人生放弃了冒险,也就放弃了无数的可能。 ...
    yichen大刀阅读 6,042评论 0 4