先解释下什么叫幂等性:
原本是数学上的概念,即使公式:f(x)=f(f(x)) 能够成立的数学性质。用在编程领域,则意为对同一个系统,使用同样的条件,一次请求和重复的多次请求对系统资源的影响是一致的。
1.一个幂等的创建订单服务,无论创建订单的请求发送多少次,正确的结果是,数据库只有一条新创建的订单记录。
这里面就出现了一个不好解决的问题:订单服务怎么知道,发来的创建订单的请求是重复的呢?
在插入数据库之前,先查询数据库是否存在重复订单,行不行?不太行,因为很难用sql的条件定义“重复的订单”,订单用户一样、订单商品一样、订单价格一样,就认定为重复订单?不一定,若用户就是连续下了2个重复的订单呢,所以这个方式说起来容易,实际上很难实现。
很多电商采用的是思路是,利用数据库表主键唯一约束特性,在插入数据时带上主键,如果重复插入多次,就会失败,这就能保证订单服务的幂等性了。
具体做法是:
给订单系统增加个“生成订单号”的系统,在用户创建订单前先去生成订单号,在用户提交订单的时候,在创建订单的请求中带上这个订单号。
需要注意一个实际问题:如果数据库是因为重复订单号导致插入订单失败,订单服务不要把错误信息返回给前端,否则就会出现这样的问题:用户创建订单,页面提示创建失败,而实际上订单却创建成功了。正确的做法是,遇到这种情况,订单服务直接返回创建成功。
2.如何解决ABA的问题?
各种更新订单的服务一样也要具有幂等性。比如支付、发货等过程,都需要对订单状态做update操作,在高并发情况下,就会出现ABA的问题。
那什么是ABA问题?比如有甲乙2个请求,一个是将订单状态更新为A,另一个是更新为B,这是正常情况。那在高并发下可能出现:订单状态在更新为A后,没来得及把响应告诉甲,另一请求又更新成了B,由于甲没收到响应,(重试机制)又发起了请求,又把B更新成了A,这个就是ABA问题,那怎么解决呢?
我们可以给订单表新增一列:version版本号来限制,每次update时,同时将version自增1,在update时,带上条件version,这样就可以把每次请求做版本区分,来达到解决ABA问题,如下sql:
update table set status=B,version=version+1 where id=1 and version=3
每次查询时,都将version返回给前端,进行修改时,需要再将version放到更新请求参数中。