基于 Redis 实现顺序号段的订单号生成方案
mhr18 2024-12-01 09:20 36 浏览 0 评论
在高并发场景中生成唯一且顺序递增的订单号时,如果频繁与数据库或 Redis 交互,性能可能会成为瓶颈。为了解决这个问题,我们可以设计一种从 Redis 预获取顺序号段并在本地缓存中生成订单号的方案。这种方法可以减少 Redis 访问频率,同时保持订单号的唯一性和顺序性。
本文将详细介绍如何实现该方案,包括设计原理、实现步骤、优化策略及注意事项。
1. 需求分析
订单号的生成规则如下:
- 格式:yyyyMMdd + 顺序号,如 20241016 001 表示2024年10月16日生成的第1个订单。
- 顺序号:每天的顺序号从 1 开始递增,并按订单生成的顺序依次递增。
- 在高并发情况下,要求订单号生成具有高性能和顺序性,且顺序号不能重复。
为提升性能,我们引入本地缓存的概念:
- Redis 存储: Redis 维护一个全局的顺序号计数器,每次从 Redis 获取一定范围的顺序号(如1-100)。
- 本地缓存: 启动时预先从 Redis 获取一个顺序号段,并在本地缓存中生成订单号。当本地缓存用尽时,再从 Redis 获取一个新的顺序号段。
2. 方案设计
2.1 设计思路
- Redis 作为全局顺序号生成器: 每次从 Redis 获取一段连续的顺序号(如1-100),并将其存储在本地缓存中。
- 本地缓存顺序号: 服务器从 Redis 获取顺序号段后,缓存在本地。在本地缓存的顺序号段用尽之前,所有的订单ID生成只需在本地完成,无需频繁访问 Redis。
- 顺序号段更新: 当本地缓存即将耗尽时,重新从 Redis 获取下一个顺序号段,确保顺序号的连续性。
2.2 Redis 数据结构
我们可以使用 Redis 的 INCRBY 命令来递增顺序号。每次服务器向 Redis 请求时,可以一次性递增指定数量的顺序号并将结果返回。
- 键名:使用当前日期作为键名,如 order:20241016,以便每天的顺序号从 1 开始。
- 值:当前顺序号,通过 INCRBY 实现。
2.3 本地缓存策略
- 初始顺序号段: 启动时,从 Redis 获取一个顺序号段并缓存在本地。假设初始获取的顺序号段是 1-100。
- 使用顺序号段: 每次生成订单号时,使用本地缓存中的顺序号,生成订单ID为 yyyyMMdd + 顺序号。
- 顺序号段耗尽: 当顺序号段即将用尽时(如剩余不足10个号),从 Redis 获取新的顺序号段(如 101-200),并继续在本地生成订单号。
2.4 顺序号生成流程图
- 初始化:
启动时从 Redis 获取当天的顺序号段(如 1-100)。
将顺序号段缓存在本地内存中,供后续订单生成使用。
- 订单生成:
当有新的订单请求时,从本地缓存中获取顺序号,并拼接当前日期,生成订单号。
如果本地缓存即将用尽(如剩余不足 10 个顺序号),向 Redis 请求新的顺序号段。
- 顺序号段更新:
从 Redis 获取新的一段顺序号(如 101-200),更新本地缓存。
3. 实现步骤
3.1 Redis 配置
在 Redis 中,我们通过 INCRBY 操作来实现全局顺序号的递增。
# Redis 命令示例
INCRBY order:20241016 100
该命令将 order:20241016 的值增加 100,并返回新的值。
3.2 代码实现
我们可以使用 Java 和 Spring Boot 来实现该方案,具体代码如下:
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
@Service
public class OrderIdService {
@Autowired
private StringRedisTemplate redisTemplate;
private static final int THRESHOLD = 10; // 缓存即将用尽的临界值
private int currentOrderNumber = 0;
private int maxOrderNumber = 0;
private Lock lock = new ReentrantLock();
/**
* 获取订单ID
*/
public String getOrderId() {
lock.lock();
try {
// 如果缓存中的顺序号即将用尽,重新从 Redis 获取新的顺序号段
if (currentOrderNumber >= maxOrderNumber - THRESHOLD) {
fetchOrderNumbersFromRedis();
}
// 生成订单ID
String date = LocalDate.now().format(DateTimeFormatter.ofPattern("yyyyMMdd"));
return date + String.format("%03d", currentOrderNumber++);
} finally {
lock.unlock();
}
}
/**
* 从 Redis 获取顺序号段
*/
private void fetchOrderNumbersFromRedis() {
String date = LocalDate.now().format(DateTimeFormatter.ofPattern("yyyyMMdd"));
String key = "order:" + date;
// 每次从 Redis 获取100个顺序号
Long nextOrderNumber = redisTemplate.opsForValue().increment(key, 100);
if (nextOrderNumber != null) {
currentOrderNumber = nextOrderNumber.intValue() - 99; // 下一个顺序号的起始值
maxOrderNumber = nextOrderNumber.intValue(); // 顺序号段的最大值
}
}
}
3.3 使用示例
@Autowired
private OrderIdService orderIdService;
public void createOrder() {
String orderId = orderIdService.getOrderId();
System.out.println("生成的订单ID: " + orderId);
// 继续处理订单的其他逻辑
}
3.4 Redis 键失效处理
为防止 Redis 中存储的键无限增长,可以为每天的顺序号设置过期时间。通过 EXPIRE 命令,可以将键的生命周期设置为 24 小时。
EXPIRE order:20241016 86400
4. 优化策略
4.1 顺序号段预加载
为了避免在高并发场景下频繁访问 Redis,可以设置更高的缓存预加载阈值。例如,每次从 Redis 获取更大的顺序号段(如 500-600)以减少 Redis 的交互次数。
4.2 异步刷新顺序号段
当本地缓存即将耗尽时,可以通过异步线程预先向 Redis 请求新的一段顺序号,避免在订单生成的关键路径中等待 Redis 响应。
4.3 本地缓存多线程安全
在多线程环境下,为了保证顺序号的安全性,使用 ReentrantLock 对顺序号的读取和更新进行加锁,避免并发问题。
5. 注意事项
5.1 Redis 可用性
系统对 Redis 的依赖较大,因此需要确保 Redis 的高可用性。例如,可以配置 Redis 哨兵模式或集群模式,保障系统在 Redis 故障时能够快速恢复。
5.2 时间同步问题
如果服务器的时间不同步,可能会导致多个服务器生成不同日期的订单号,造成混乱。因此,确保所有服务器的时间同步非常重要,可以考虑使用 NTP 服务。
5.3 顺序号冲突
在高并发情况下,如果 Redis 存在短暂不可用的情况,可能会导致顺序号段无法及时刷新。为了防止这种情况,可以在业务逻辑中增加唯一性检查。
6. 总结
通过 Redis 预先获取顺序号段并在本地缓存顺序号生成订单ID,可以大幅提高系统在高并发下的性能表现。Redis 的 INCRBY 操作能够高效地生成全局唯一的顺序号,而本地缓存机制则减少了与 Redis 交互的频率,使得订单ID生成更加高效。
相关推荐
- Dubai's AI Boom Lures Global Tech as Emirate Reinvents Itself as Middle East's Silicon Gateway
-
AI-generatedimageAsianFin--Dubaiisrapidlytransformingitselffromadesertoilhubintoaglob...
- OpenAI Releases o3-pro, Cuts o3 Prices by 80% as Deal with Google Cloud Reported to Make for Compute Needs
-
TMTPOST--OpenAIisescalatingthepricewarinlargelanguagemodel(LLM)whileseekingpartnershi...
- 黄仁勋说AI Agent才是未来!但究竟有些啥影响?
-
,抓住风口(iOS用户请用电脑端打开小程序)本期要点:详解2025年大热点你好,我是王煜全,这里是王煜全要闻评论。最近,有个词被各个科技大佬反复提及——AIAgent,智能体。黄仁勋在CES展的发布...
- 商城微服务项目组件搭建(五)——Kafka、Tomcat等安装部署
-
1、本文属于mini商城系列文档的第0章,由于篇幅原因,这篇文章拆成了6部分,本文属于第5部分2、mini商城项目详细文档及代码见CSDN:https://blog.csdn.net/Eclipse_...
- Python+Appium环境搭建与自动化教程
-
以下是保姆级教程,手把手教你搭建Python+Appium环境并实现简单的APP自动化测试:一、环境搭建(Windows系统)1.安装Python访问Python官网下载最新版(建议...
- 零配置入门:用VSCode写Java代码的正确姿
-
一、环境准备:安装JDK,让电脑“听懂”Java目标:安装Java开发工具包(JDK),配置环境变量下载JDKJava程序需要JDK(JavaDevelopmentKit)才能运行和编译。以下是两...
- Mycat的搭建以及配置与启动(mycat2)
-
1、首先开启服务器相关端口firewall-cmd--permanent--add-port=9066/tcpfirewall-cmd--permanent--add-port=80...
- kubernetes 部署mysql应用(k8s mysql部署)
-
这边仅用于测试环境,一般生产环境mysql不建议使用容器部署。这里假设安装mysql版本为mysql8.0.33一、创建MySQL配置(ConfigMap)#mysql-config.yaml...
- Spring Data Jpa 介绍和详细入门案例搭建
-
1.SpringDataJPA的概念在介绍SpringDataJPA的时候,我们首先认识下Hibernate。Hibernate是数据访问解决技术的绝对霸主,使用O/R映射(Object-Re...
- 量子点格棋上线!“天衍”邀您执子入局
-
你是否能在策略上战胜量子智能?这不仅是一场博弈更是一次量子智力的较量——量子点格棋正式上线!试试你能否赢下这场量子智局!游戏玩法详解一笔一画间的策略博弈游戏目标:封闭格子、争夺领地点格棋的基本目标是利...
- 美国将与阿联酋合作建立海外最大的人工智能数据中心
-
当地时间5月15日,美国白宫宣布与阿联酋合作建立人工智能数据中心园区,据称这是美国以外最大的人工智能园区。阿布扎比政府支持的阿联酋公司G42及多家美国公司将在阿布扎比合作建造容量为5GW的数据中心,占...
- 盘后股价大涨近8%!甲骨文的业绩及指引超预期?
-
近期,美股的AI概念股迎来了一波上升行情,微软(MSFT.US)频创新高,英伟达(NVDA.US)、台积电(TSM.US)、博通(AVGO.US)、甲骨文(ORCL.US)等多股亦出现显著上涨。而从基...
- 甲骨文预计新财年云基础设施营收将涨超70%,盘后一度涨8% | 财报见闻
-
甲骨文(Oracle)周三盘后公布财报显示,该公司第四财季业绩超预期,虽然云基建略微逊于预期,但管理层预计2026财年云基础设施营收预计将增长超过70%,同时资本支出继上年猛增三倍后,新财年将继续增至...
- Springboot数据访问(整合MongoDB)
-
SpringBoot整合MongoDB基本概念MongoDB与我们之前熟知的关系型数据库(MySQL、Oracle)不同,MongoDB是一个文档数据库,它具有所需的可伸缩性和灵活性,以及所需的查询和...
- Linux环境下,Jmeter压力测试的搭建及报错解决方法
-
概述 Jmeter最早是为了测试Tomcat的前身JServ的执行效率而诞生的。到目前为止,它的最新版本是5.3,其测试能力也不再仅仅只局限于对于Web服务器的测试,而是涵盖了数据库、JM...
你 发表评论:
欢迎- 一周热门
- 最近发表
-
- Dubai's AI Boom Lures Global Tech as Emirate Reinvents Itself as Middle East's Silicon Gateway
- OpenAI Releases o3-pro, Cuts o3 Prices by 80% as Deal with Google Cloud Reported to Make for Compute Needs
- 黄仁勋说AI Agent才是未来!但究竟有些啥影响?
- 商城微服务项目组件搭建(五)——Kafka、Tomcat等安装部署
- Python+Appium环境搭建与自动化教程
- 零配置入门:用VSCode写Java代码的正确姿
- Mycat的搭建以及配置与启动(mycat2)
- kubernetes 部署mysql应用(k8s mysql部署)
- Spring Data Jpa 介绍和详细入门案例搭建
- 量子点格棋上线!“天衍”邀您执子入局
- 标签列表
-
- oracle位图索引 (74)
- oracle批量插入数据 (65)
- oracle事务隔离级别 (59)
- oracle 空为0 (51)
- oracle主从同步 (56)
- oracle 乐观锁 (53)
- redis 命令 (78)
- php redis (88)
- redis 存储 (66)
- redis 锁 (69)
- 启动 redis (66)
- redis 时间 (56)
- redis 删除 (67)
- redis内存 (57)
- redis并发 (52)
- redis 主从 (69)
- redis 订阅 (51)
- redis 登录 (54)
- redis 面试 (58)
- 阿里 redis (59)
- redis 搭建 (53)
- redis的缓存 (55)
- lua redis (58)
- redis 连接池 (61)
- redis 限流 (51)