浅谈幂等设计 | 京东云技术团队
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
作者:京东零售 李泽阳
来源:京东云开发者社区 转载请注明来源
关注公众号
低调大师中文资讯倾力打造互联网数据资讯、行业资源、电子商务、移动互联网、网络营销平台。
持续更新报道IT业界、互联网、市场资讯、驱动更新,是最及时权威的产业资讯及硬件资讯报道平台。
转载内容版权归作者及来源网站所有,本站原创内容转载请注明来源。
-
上一篇
浅谈Mysql读写分离的坑以及应对的方案 | 京东云技术团队
一、主从架构 为什么我们要进行读写分离?个人觉得还是业务发展到一定的规模,驱动技术架构的改革,读写分离可以减轻单台服务器的压力,将读请求和写请求分流到不同的服务器,分摊单台服务的负载,提高可用性,提高读请求的性能。 上面这个图是一个基础的Mysql的主从架构,1主1备3从。这种架构是客户端主动做的负载均衡,数据库的连接信息一般是放到客户端的连接层,也就是说由客户端来选择数据库进行读写 上图是一个带proxy的主从架构,客户端只和proxy进行连接,由proxy根据请求类型和上下文决定请求的分发路由。 两种架构方案各有什么特点: 1.客户端直连架构,由于少了一层proxy转发,所以查询性能会比较好点儿,架构简单,遇到问题好排查。但是这种架构,由于要了解后端部署细节,出现主备切换,库迁移的时候客户端都会感知到,并且需要调整库连接信息 2.带proxy的架构,对客户端比较友好,客户端不需要了解后端部署细节,连接维护,后端信息维护都由proxy来完成。这样的架构对后端运维团队要求比较高,而且proxy本身也要求高可用,所以整体架构相对来说比较复杂 但是不论使用哪种架构,由于主从之间存在...
-
下一篇
GaussDB之应用无损透明(ALT)
1. 背景 GaussDB作为一款企业级分布式数据库,提供了“同城跨AZ双活、两地三中心、双集群强一致”等极致的高可用容灾能力。当某个数据库节点由于故障无法对外提供服务时,为了继续保证数据库服务的可用性,JDBC驱动会将业务后续的数据库连接请求发送到其它可用节点上。但故障发生后,已经与故障节点建立会话的连接无法自动切换到可用节点上,导致使用这些连接的业务单元发生报错。如果业务单元缺少连接重试或业务一致性校验,可能会引起应用中断,甚至业务数据不一致的问题,造成用户严重的业务损失。 因此,华为云GaussDB数据库提供了一种在数据库故障情况下的客户端连接转移方案 —— ALT(Application Lossness Transparent,应用无损透明)。该方案的原理是,当数据库集群的某个节点由于故障无法对外提供服务,若此时集群内还存在其它可用节点,则将故障节点上的会话连接自动迁移到目标节点上,客户端无需再次发出连接请求,仍然可以继续执行数据库操作。整个过程中,客户端应用程序是无感知的,就像是经历了一次略有延迟的SQL请求处理,极大地提高了数据库服务的可用性。 2. 技术架构 我们先来看...
相关文章
文章评论
共有0条评论来说两句吧...
文章二维码
点击排行
推荐阅读
最新文章
- SpringBoot2编写第一个Controller,响应你的http请求并返回结果
- Linux系统CentOS6、CentOS7手动修改IP地址
- 设置Eclipse缩进为4个空格,增强代码规范
- 2048小游戏-低调大师作品
- CentOS6,CentOS7官方镜像安装Oracle11G
- CentOS8,CentOS7,CentOS6编译安装Redis5.0.7
- Windows10,CentOS7,CentOS8安装MongoDB4.0.16
- Jdk安装(Linux,MacOS,Windows),包含三大操作系统的最全安装
- CentOS7,CentOS8安装Elasticsearch6.8.6
- Springboot2将连接池hikari替换为druid,体验最强大的数据库连接池



微信收款码
支付宝收款码