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

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\] 分布式系统互斥性与幂等性问题的分析与解决 [zhuanlan.zhihu.com/p/22820761](https://link.juejin.cn?target=https%3A%2F%2Fzhuanlan.zhihu.com%2Fp%2F22820761 "https://zhuanlan.zhihu.com/p/22820761") \[2\] 高并发下接口幂等性解决方案 [blog.csdn.net/u011635492/...](https://link.juejin.cn?target=https%3A%2F%2Fblog.csdn.net%2Fu011635492%2Farticle%2Fdetails%2F81058153 "https://blog.csdn.net/u011635492/article/details/81058153") \[3\] 幂等性问题和解决方法 [blog.csdn.net/qq_32020035...](https://link.juejin.cn?target=https%3A%2F%2Fblog.csdn.net%2Fqq_32020035%2Farticle%2Fdetails%2F105448889 "https://blog.csdn.net/qq_32020035/article/details/105448889") \[4\] 雪花算法 [www.cnblogs.com/grasp/p/123...](https://link.juejin.cn?target=https%3A%2F%2Fwww.cnblogs.com%2Fgrasp%2Fp%2F12309726.html "https://www.cnblogs.com/grasp/p/12309726.html") \[5\] 聊聊开发中幂等问题 [segmentfault.com/a/119000001...](https://link.juejin.cn?target=https%3A%2F%2Fsegmentfault.com%2Fa%2F1190000018808510 "https://segmentfault.com/a/1190000018808510") > 作者:京东零售 李泽阳 > > 来源:京东云开发者社区 转载请注明来源

相关推荐
想用offer打牌13 小时前
一站式了解数据库三大范式(库表设计基础)
数据库·后端·面试
甘露s13 小时前
MySQL深入之索引、存储引擎和SQL优化
数据库·sql·mysql
偶遇急雨洗心尘13 小时前
记录一次服务器迁移时,数据库版本不一致导致sql函数报错和系统redirect重定向丢失域名问题
运维·服务器·数据库·sql
Arva .14 小时前
MySQL 的存储引擎
数据库·mysql
一缕猫毛14 小时前
Flink demo代码
java·大数据·flink
Logic10114 小时前
《Mysql数据库应用》 第2版 郭文明 实验5 存储过程与函数的构建与使用核心操作与思路解析
数据库·sql·mysql·学习笔记·计算机网络技术·形考作业·国家开放大学
Hello.Reader14 小时前
Flink ML 基本概念Table API、Stage、Pipeline 与 Graph
大数据·python·flink
小二·14 小时前
MyBatis基础入门《十六》企业级插件实战:基于 MyBatis Interceptor 实现 SQL 审计、慢查询监控与数据脱敏
数据库·sql·mybatis
bing.shao14 小时前
Golang WaitGroup 踩坑
开发语言·数据库·golang
pale_moonlight14 小时前
十一、Flink基础环境实战
大数据·flink