浅谈幂等设计 | 京东云技术团队

京东云开发者
• 阅读 420

1 幂等性

一句话,幂等就是一个执行操作,无论执行多少次,产生的效果和返回的结果都是一样的。

2 为什么要实现幂等性?

如今随着互联网技术快速发展,业务越来越复杂,系统的高并发和关键数据的场景越来越多。

在分布式系统中,机器宕机和消息丢失也是需要重点关注的问题,其中的一个典型就是幂等性问题。

想想看,一个对外暴露的接口会面领很多次请求,如果不能保证幂等性会带来什么样的后果?

微信进行一次扣款操作,应该只扣用户一次钱,当遇到网络故障或系统bug,如果没有实现幂等性扣多了你会不会直接“C语言”投诉?

当然,有些接口是天然保证幂等性的,比如查询操作、删除操作。有些对数据的修改是一个常量,无其他操作,也是具有幂等性的。修改操作可能幂等可能不幂等。

SELECT col1 FROM tab1 WHERE col2 = 2UPDATE tab1 SET col1 = 1 WHERE col2 = 2UPDATE tab1 SET col1 = col1 + 1 WHERE col2 = 2

这三个sql只有第三个不是幂等的。

POST请求天生就不是一个幂等操作,每次调用都会在系统中产生新的资源,想要幂等就必须在业务中实现。

需要避免的是,幂等性和并发安全不是一回事。当同一笔订单即使你不停的提交支付,如果扣了不止一次钱,就说明该操作不幂等。

而有多笔订单同时进行支付,最后扣除的金额不是这么多笔金额的总和,说明该操作有并发安全问题。这是两个维度的问题,应该分开讨论解决。

3 如何实现幂等性?

(1)数据库防重

利用数据表唯一索引的特性,当并发时新增报错时,再查询一次,数据已经存在,就避免了脏数据的新增。但注意,不要将uuid作为索引字段,其大小和类型对于索引而言都会导致速度非常慢。

常见的场景,比如博客/微博系统点赞,一个用户对一个微博点赞,就把用户id与该博文id绑定,后续该用户再对该博文点赞就无法插入。再比如金融账户,可以通过在账户表中增加唯一索引来存储用户id,即使重复操作一个用户也只能拥有一个账户。

(2)token令牌机制

token机制是适用范围最广泛的一种幂等设计。虽然实现方式有很多种,但核心思想就是每次操作都生成一个唯一token凭证,服务器通过这个唯一凭证确保同样的操作不会被执行多次。

具体可以分为两个阶段,获取token和使用token。每次接口请求前先获取一个token,然后在下次请求时在请求的header体中加上这个token,后端进行校验,如果验证通过则删除token,下次请求再次判断token。如果在redis缓存的帮助下,流程图如下:

浅谈幂等设计 | 京东云技术团队

(3)分布式锁

数据库防重表可以通过分布式锁代替,相比去重表,将放并发做到了缓存中,效率更高。局限性都是同一时间只能完成一次请求。

比如某些业务处理流程很长,要求不能并发执行,可以在流程执行之前根据某个标志(用户ID+后缀等)获取分布式锁,其他流程执行时获取锁就会失败,也就是同一时间该流程只能有一个能执行成功,执行完成后,释放分布式锁。

4 幂等的优缺点

优点:

业务需要

缺点:

(1)客户端处理逻辑得以简化,但服务端控制幂等逻辑变得更加复杂;

(2)把并发执行变成改为串行执行,降低了执行效率。

5 扩展

分布式自增ID可以借鉴Snowflake算法,优点是高性能、低延迟、按时间有序;缺点是需要独立的开发和部署。

其结构如下:

浅谈幂等设计 | 京东云技术团队

  • 最高位是符号位,始终为0,不可用。
  • 41位的时间序列,精确到毫秒级,41位的长度可以使用 (1L << 41) / (1000L * 60 * 60 * 24 * 365) = 69年。时间位还有一个很重要的作用是可以根据时间进行排序。注意,41位时间截不是存储当前时间的时间截,而是存储时间截的差值(当前时间截 - 开始时间截) 后得到的值,这里的的开始时间截,一般是我们的id生成器开始使用的时间,由我们程序来指定的。
  • 10位的机器标识,10位的长度最多支持部署1024个节点。
  • 12位的计数序列号,序列号即一系列的自增id,可以支持同一节点同一毫秒生成多个ID序号,12位的计数序列号支持每个节点每毫秒产生4096个ID序号。

加起来刚好64位,为一个Long型。这个算法很简洁,但依旧是一个很好的ID生成策略。

参考文献:

[1] 分布式系统互斥性与幂等性问题的分析与解决

https://zhuanlan.zhihu.com/p/22820761

[2] 高并发下接口幂等性解决方案

https://blog.csdn.net/u011635492/article/details/81058153

[3] 幂等性问题和解决方法

https://blog.csdn.net/qq_32020035/article/details/105448889

[4] 雪花算法

https://www.cnblogs.com/grasp/p/12309726.html

[5] 聊聊开发中幂等问题

https://segmentfault.com/a/1190000018808510

作者:京东零售 李泽阳

来源:京东云开发者社区 转载请注明来源

点赞
收藏
评论区
推荐文章
皕杰报表之UUID
​在我们用皕杰报表工具设计填报报表时,如何在新增行里自动增加id呢?能新增整数排序id吗?目前可以在新增行里自动增加id,但只能用uuid函数增加UUID编码,不能新增整数排序id。uuid函数说明:获取一个UUID,可以在填报表中用来创建数据ID语法:uuid()或uuid(sep)参数说明:sep布尔值,生成的uuid中是否包含分隔符'',缺省为
peter peter
3年前
Go:分布式锁实现原理与最佳实践
分布式锁应用场景很多应用场景是需要系统保证幂等性的(如api服务或消息消费者),并发情况下或消息重复很容易造成系统重入,那么分布式锁是保障幂等的一个重要手段。另一方面,很多抢单场景或者叫交易撮合场景,如dd司机抢单或唯一商品抢拍等都需要用一把“全局锁”来解决并发造成的问题。在防止并发情况下造成库存超卖的场景,也常用分布式锁来解决。实现
浅谈复杂业务系统的架构设计 | 京东云技术团队
复杂系统的架构设计不是一蹴而就的,合适的才是正确的。希望本文能够对您在进行复杂系统设计时有一定的参考意义。
Peter20 Peter20
3年前
mysql中like用法
like的通配符有两种%(百分号):代表零个、一个或者多个字符。\(下划线):代表一个数字或者字符。1\.name以"李"开头wherenamelike'李%'2\.name中包含"云",“云”可以在任何位置wherenamelike'%云%'3\.第二个和第三个字符是0的值wheresalarylike'\00%'4\
Wesley13 Wesley13
3年前
FLV文件格式
1.        FLV文件对齐方式FLV文件以大端对齐方式存放多字节整型。如存放数字无符号16位的数字300(0x012C),那么在FLV文件中存放的顺序是:|0x01|0x2C|。如果是无符号32位数字300(0x0000012C),那么在FLV文件中的存放顺序是:|0x00|0x00|0x00|0x01|0x2C。2.  
Wesley13 Wesley13
3年前
mysql设置时区
mysql设置时区mysql\_query("SETtime\_zone'8:00'")ordie('时区设置失败,请联系管理员!');中国在东8区所以加8方法二:selectcount(user\_id)asdevice,CONVERT\_TZ(FROM\_UNIXTIME(reg\_time),'08:00','0
Stella981 Stella981
3年前
IM消息ID技术专题(六):深度解密滴滴的高性能ID生成器(Tinyid)
1、引言在中大型IM系统中,聊天消息的唯一ID生成策略是个很重要的技术点。不夸张的说,聊天消息ID贯穿了整个聊天生命周期的几乎每一个算法、逻辑和过程,ID生成策略的好坏有可能直接决定系统在某些技术点上的设计难易度。有中小型IM场景下,消息ID可以简单处理,反正只要唯一就行,而中大型场景下,因为要考虑到分布式的性能、一致性等,所以要考虑的问题
Wesley13 Wesley13
3年前
MySQL部分从库上面因为大量的临时表tmp_table造成慢查询
背景描述Time:20190124T00:08:14.70572408:00User@Host:@Id:Schema:sentrymetaLast_errno:0Killed:0Query_time:0.315758Lock_