幂等性
简介
幂等性是源自数学上的概念,本意是一个函数多次调用的结果是一次调用的结果是相等的,用公式表示即f(f(x))=f(x)。例如一个数字乘以1就是一个幂等函数,k*1*1*1...*1=k*1,无论该函数执行多少次和执行一次是相同的。
在分布式环境下,幂等性是基本的要求,因为分布式场景下失败或者未知结果是系统调用的常态,尤其对于未知结果而言,调用方不知道调用是否成功,一般会发起重试,而且对于很多中间件,例如消息队列也不能保证消息仅仅被触发一次,所以系统如果不支持幂等性,就会出现逻辑以及数据错误。
例如用户的转账,如果不支持幂等性,会出现一笔转账执行多次的可能性,不支持幂等是金融等系统出现资金损失最常见的场景。
原理
幂等性本质是数据的幂等性,调用多次方法之后,数据不能因为重复执行而出现数据问题。
唯一主键
数据从使用场景上可以分为状态型数据和流水性数据。
流水型数据
流水性数据即每次业务业务操作都会产生一条明细流水的数据,例如评论数据,订单数据,消息数据,甚至点赞操作都可以产生一条流水,流水数据一般会有一个业务ID,该业务ID和本次业务操作关联,成为业务ID。
流水性数据一般需要幂等的场景都是插入操作,因为流水型数据一旦插入,其业务信息是不可变的,唯一可变的一般是业务状态,而业务状态一般通过状态机来保证状态的正确性。
流水型数据的幂等一般是通过业务ID实现,即通过业务ID的唯一性来实现幂等,业务ID的唯一性一般通过数据库的唯一主键进行约束。唯一主键的强约束会在系统被重复插入的时候抛出异常。
因为流水型数据的插入可能会经过多个系统,所以流水型数据的写入操作要在数据提交入口的时候就尽可能带有业务ID,但是部分业务场景没办法在插入数据前产生业务ID,解决方案一般是以下两种:
提交页面生成唯一的token,该token可以而且仅可以使用一次,然后通过该token换取业务ID,如果token失效,则本次提交请求失败。通过token+ID的模式确保在提交过程中一定只能生成一个有效的ID。
通过防重设计,一般通过分布式锁实现,确保一定时间之内只能产生一次提交操作,通过这种类似全局锁定的逻辑确保一段时间之内只能有一个有效的业务ID。
状态型数据
状态型数据即库存数据,账户数据,点赞数等数据,这些数据的生命周期较长,在很长的周期内都会发生更新的操作,因为状态型数据的ID一般是某个业务实体的ID,而不是本次业务操作的ID,但是本次的更新一般可以和本次业务操作关联,所以状态型数据的幂等一般也依赖该次的流水操作。
例如库存的更新和订单操作关联,放在同一个事务,依赖流水型订单插入时候唯一主键的限制来同时保证状态行数据也会仅仅被修改一次。