欢迎您光临本小站。希望您在这里可以找到自己想要的信息。。。

系统幂等设计浅谈

架构&设计模式 water 1819℃ 0评论

幂等设计在分布式系统设计中占有很重要的地位,是实现数据一致性和事务完整性的重要手段。近期在优化交易系统,系统中很多地方用到了幂等设计,遂对其进行了总结。

幂等定义:

在编程中一个幂等操作的特点是其任意多次执行所产生的影响均与一次执行的影响相同。幂等函数,或幂等方法,是指可以使用相同参数重复执行,并能获得相同结果的函数。这些函数不会影响系统状态,也不用担心重复执行会对系统造成改变。例如 “setTrue()” 函数就是一个幂等函数,无论多次执行,其结果都是一样的。

值得注意的是许多人认为幂等就是防重,实际上这是两个不同的概念,防重是实现幂等的一种方式。防重是指提交多次相同的请求到后台,系统必须能够去重,防止重复执行;而幂等,则是在多个相同的请求同时或者先后到达后台,即使重复执行,系统也必须始终提供与一致的状态,而不引入副作用。 还有一种误解是认为幂等就是多次调用返回的结果是相同的,其实幂等侧重的是多次相同的调用对系统不产生副作用,一个查询接口多次调用返回的内容也可能不一样。若是在查询的时候对记录访问数进行加 1 操作,这就不是幂等,虽然返回结果相同,但对数据访问量产生了副作用。 为了避免理解上的偏差,接下来的讨论幂等所指的副作用是程序设计者所关注的对象,毕竟 SELECT 操作也会有几条日志产生。

幂等实现:

CRUD中

  • 读操作

一般的读 API、getXxx()、SELECT 等读操作天然具有幂等性。

  • 删除操作

记录删一次和删除多次结果都是一样的,除非需要返回删除的记录。

写操作的幂等设计

数据库唯一索引

例如一个用户只有一个账户id,再次请求创建账户时返回原账户id 可以通过唯一索引或者唯一组合索引避免添加脏数据 数据库语句可以通过: INSERT xxx ON DUPLICATE KEY UPDATE xxx 或者 REPLACE xxx 的方式。不过不建议在生产环境这么用。

乐观锁、悲观锁、分布式锁,保证操作最终只会被执行一次都行。

防重表

使用业务唯一标识作为防重表的唯一索引,每次请求都根据业务唯一标识向去重表中插入一条数据。执行完请求操作,删除防重表中的数据。重复的请求因为表中唯一索引而插入失败,则返回操作失败,直到第一次的请求完成。 可以看出防重表作用是加锁的功能,主要是避免相同的请求多次重发。

Token机制

主要用于与前端进行的交互,前端发起请求前向后台申请 Token,发起请求时带上 Token,Token 一次有效,有过期时间。 后台生成 Token 后记录在存储中,前端使用时看 Token 是否存在存储,不存在则为非法请求,存在则从存储中删除该 Token 并执行业务操作。 使用 Token 一是可以防止网络重发包,二是可以利用 Token 进行限流。

数据版本号

先查询数据当前版本,再用此版本进行数据写操作,有点类似于乐观锁。 要注意的是读和写之间要加锁,避免并发操作的情况。

状态机

一般业务会涉及多个状态之间的流转,这就会有状态机的概念。通过状态机保证幂等是指如果状态机已经处于下一状态,而此时又来了上一个状态的请求,这时可以不进行处理。

上面列举了一些常见的幂等设计方式,其实注意观察的话这些设计方式都有共通点,抽象出来就是幂等的两个要素: 1. 输入唯一性 2. 处理的唯一性

我们可以围绕这两个要素来实现幂等 举个例子: 要设计一个充值接口,幂等的目标是避免上游的重试操作导致重复充值

从输入的唯一性来设计,让请求者每次请求带上唯一标识

例如: 我们可以让调用者申请 Token,使用相同 Token 只有一次能成功; 也可以使用防重表,将请求者的唯一标识作为唯一索引插入防重表,此后重放的操作; 还可以用分布式锁,将唯一标识存在第三方的系统; 这几种方案本质都是一样的,都是保证输入唯一,区别在于唯一标识谁来生成,存储在哪

从处理的唯一性来考虑,我们可以让请求者指定结果

例如: 抹除欠费,余额为负则边为 0 先查询余额再指定结果,余额从 500 变为 600 账户加版本号,先查询当前版本为 9 ,再在 10 个版本把余额加 100 这个例子可能举得不是很好,会存在并发上的一些问题,所以也没人会这么设计充值接口。

正常来说用户充值是先生成充值订单,再根据订单去账户生成充值流水,用流水号来确认充值 这么设计的原则一是将用户的一次充值请求绑定到他可感知到的一个唯一订单上,订单状态机可避免用户重复提交

所以,关键是要根据业务特性和系统架构来选择合适的方案来实现幂等。

转载请注明:学时网 » 系统幂等设计浅谈

喜欢 (2)or分享 (0)

您必须 登录 才能发表评论!