互联网业务幂等性实现之基于MySQL
背景
在互联网业务领域中,我们经常会遇到应用到请求幂等性问题,即多次重复请求,所得到的结果,和一次请求一致。
以某互联网电商的取消订单为例子,当订单取消,需要返回给消费者下单所消费的虚拟产品,如优惠券、红包、京豆等。通过幂等形式,确保返还给消费者的权益不多、不少。
那幂等性具体开发是怎么实现的呢?本文带来基于MySQL的UNIQUE KEY的实现方案。
实现
众所周知,UNIQUE
KEY是数据库中的唯一索引,数据库的记录中不允许有重复的值,我们可以利用这点,在处理业务前,先进行唯一索引数据(如订单id)的插入操作:
插入成功,说明是第一次插入,正常处理业务;插入失败,说明该业务逻辑已经处理过了,不做处理,提前返回;
如此,即可实现幂等性。
1.数据库设计
幂等性辅助表设计如下:
CREATE TABLE `idempotent_validate` (
`id` bigint NOT NULL,
`create_time` time DEFAULT NULL,
`order_id` bigint DEFAULT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `UK_orssam7fgn4uj0lo2sn4on6vg` (`order_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
其中的一个字段order_id,我们定义为唯一索引。
2.代码编写
我们主要实现了订单取消方法cancelOrder:
package com.example.idempotentmysql.service.impl;
import com.example.idempotentmysql.bean.IdempotentValidate;
import com.example.idempotentmysql.bean.OrderInfo;
import com.example.idempotentmysql.bean.ProductInfo;
import com.example.idempotentmysql.bean.UserInfo;
import com.example.idempotentmysql.repository.IdempotentValidateRepository;
import com.example.idempotentmysql.repository.OrderInfoRepository;
import com.example.idempotentmysql.repository.ProductInfoRepository;
import com.example.idempotentmysql.repository.UserInfoRepository;
import com.example.idempotentmysql.service.OrderService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import java.util.Optional;
/**
* 订单服务
*
* @author hongcunlin
*/
@Service
public class OrderServiceImpl implements OrderService {
/**
* 日志
*/
private static final Logger LOGGER = LoggerFactory.getLogger(OrderServiceImpl.class);
/**
* 用户repository
*/
@Resource
private UserInfoRepository userInfoRepository;
/**
* 商品repository
*/
@Resource
private ProductInfoRepository productInfoRepository;
/**
* 订单repository
*/
@Resource
private OrderInfoRepository orderInfoRepository;
/**
* 幂等性校验
*/
@Resource
private IdempotentValidateRepository idempotentValidateRepository;
/**
* 取消订单(带幂等性校验)
*
* @param orderId 订单id
*/
@Override
public void cancelOrder(Long orderId) {
// 1.幂等性校验
try {
IdempotentValidate idempotentValidate = new IdempotentValidate();
idempotentValidate.setOrderId(orderId);
idempotentValidateRepository.save(idempotentValidate);
} catch (Exception e) {
LOGGER.info("订单退款幂等");
return;
}
// 2.退款
Optional
if (orderInfoOptional.isPresent()) {
OrderInfo orderInfo = orderInfoOptional.get();
Optional
Optional
if (userInfoOptional.isPresent()