社区编辑申请
注册/登录
互联网业务幂等性实现之基于MySQL
数据库 其他数据库
以某互联网电商的取消订单为例子,当订单取消,需要返回给消费者下单所消费的虚拟产品,如优惠券、红包、京豆等。通过幂等形式,确保返还给消费者的权益不多、不少。那幂等性具体开发是怎么实现的呢?本文带来基于MySQL的UNIQUE KEY的实现方案。

背景

在互联网业务领域中,我们经常会遇到应用到请求幂等性问题,即多次重复请求,所得到的结果,和一次请求一致。

以某互联网电商的取消订单为例子,当订单取消,需要返回给消费者下单所消费的虚拟产品,如优惠券、红包、京豆等。通过幂等形式,确保返还给消费者的权益不多、不少。

那幂等性具体开发是怎么实现的呢?本文带来基于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<OrderInfo> orderInfoOptional = orderInfoRepository.findById(orderId);
if (orderInfoOptional.isPresent()) {
OrderInfo orderInfo = orderInfoOptional.get();
Optional<UserInfo> userInfoOptional = userInfoRepository.findById(orderInfo.getUserId());
Optional<ProductInfo> productInfoOptional = productInfoRepository.findById(orderInfo.getProductId());
if (userInfoOptional.isPresent() && productInfoOptional.isPresent()) {
UserInfo userInfo = userInfoOptional.get();
ProductInfo productInfo = productInfoOptional.get();
userInfo.setMoney(userInfo.getMoney().add(productInfo.getPrice()));
userInfoRepository.save(userInfo);
}
}
LOGGER.info("订单成功退款");
}
}

从代码中可以看到,我们在除了订单退款前,幂等性表先拿订单id进行插入操作,若插入成功,说明是第一次取消订单,执行下面的退款逻辑;

若插入失败,说明之前已经进行过退款逻辑了,我们提前返回,不做下面的退款操作。

如此,便实现了订单退款的幂等性。

3.测试

我们对订单进行3次取消操作:

package com.example.idempotentmysql.service;


import com.example.idempotentmysql.bean.OrderInfo;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;

import javax.annotation.Resource;

@SpringBootTest
public class OrderServiceTest {

@Resource
private OrderService orderService;

@Test
public void cancelOrderTest() {
orderService.cancelOrder(6L);
orderService.cancelOrder(6L);
orderService.cancelOrder(6L);
}
}

可以看到,只有第一次是退款成功的,后面2次触发幂等性,退款失败,符合我们的预期。

其他

幂等性的实现方式还有很多种,基于MySQL的UNIQUE KEY的实现方案,实现起来相当简单,仅适合业务简单,并发量不高的场景。原因是MySQL的qps有限,且MySQL作为系统的最底层的应用,过于后置,如果最终幂等返回,比较浪费前置的业务处理所消耗的资源。

本文代码已经上传到github上了,有需要的同学可以下载参考:https://github.com/larger5/idempotent-mysql

责任编辑:武晓燕 来源: 今日头条
相关推荐

2022-05-18 23:42:08

网络安全安全分析工具

2022-05-17 14:03:42

勒索软件远程工作

2022-05-20 14:54:33

数据安全数字化转型企业

2022-04-25 11:26:16

开发SpringBoot

2022-05-17 15:51:32

数据中心运维能力基础设施

2022-05-09 15:08:56

存储厂商NFV领域华为

2022-05-11 15:08:52

驱动开发系统移植

2022-05-16 10:36:08

GitHub开源项目

2022-05-11 14:48:33

腾讯云寿险民生保险

2022-05-16 13:37:12

Sysrv僵尸网络微软

2022-05-23 10:55:19

华为数字化转型架构蓝图

2022-03-22 07:57:42

Java多线程并发

2022-04-19 11:23:26

release3.1子系统鸿蒙

2022-05-20 14:08:13

Web3元宇宙区块链

2022-05-09 11:57:39

云原生实践安全

2022-04-19 14:41:29

Oracle数据库SQL

2022-04-25 14:06:28

数据分析人工智能机器学习

2022-05-11 08:23:54

自动化测试软件测试

2022-05-11 14:05:11

区块链网络安全存储

2022-05-12 14:44:38

数据中心IT云计算

同话题下的热门内容

Abase2:字节跳动新一代高可用 NoSQL 数据库金融业分布式数据库选型及HTAP场景实践谈谈对 Database Plus 认识与畅想再有人问你什么是分库分表,直接把这篇文章发给他​基于智能数据库的自助式机器学习Flink SQL 知其所以然:SQL DDL!每个后端都应该知道的八个提升 SQL 性能的 Tips

编辑推荐

几款开源的图形化Redis客户端管理软件推荐NoSQL数据库概览及其与SQL语法的比较为什么MongoDB敢说“做以前你从未能做的事”Python操作MongoDB看这一篇就够了一文看懂分布式数据库原理和 PostgreSQL 分布式架构
我收藏的内容
点赞
收藏

51CTO技术栈公众号