基于 Redis 实现顺序号段的订单号生成方案
mhr18 2024-12-01 09:20 21 浏览 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生成更加高效。
相关推荐
- redis 7.4.3更新!安全修复+性能优化全解析
-
一、Redis是什么?为什么选择它?Redis(RemoteDictionaryServer)是一款开源的高性能内存键值数据库,支持持久化、多数据结构(如字符串、哈希、列表等),广泛应用于缓存、消...
- C# 读写Redis数据库的简单例子
-
CSRedis是一个基于C#的Redis客户端库,它提供了与Redis服务器进行交互的功能。它是一个轻量级、高性能的库,易于使用和集成到C#应用程序中。您可以使用NuGet包管理器或使用以下命令行命令...
- 十年之重修Redis原理
-
弱小和无知并不是生存的障碍,傲慢才是。--------面试者总结Redis可能都用过,但是从来没有理解过,就像一个熟悉的陌生人,本文主要讲述了Redis基本类型的使用、数据结构、持久化、单线程模型...
- 高频L2行情数据Redis存储架构设计(含C++实现代码)
-
一、Redis核心设计原则内存高效:优化数据结构,减少内存占用低延迟访问:单次操作≤0.1ms响应时间数据完整性:完整存储所有L2字段实时订阅:支持多客户端实时数据推送持久化策略:RDB+AOF保障数...
- Magic-Boot开源引擎:零代码玩转企业级开发,效率暴涨!
-
一、项目介绍基于magic-api搭建的快速开发平台,前端采用Vue3+naive-ui最新版本搭建,依赖较少,运行速度快。对常用组件进行封装。利用Vue3的@vue/compiler-sfc单文...
- 项目不行简历拉胯?3招教你从面试陪跑逆袭大厂offer!
-
项目不行简历拉胯?3招教你从面试陪跑逆袭大厂offer!老铁们!是不是每次面试完都感觉自己像被大厂面试官婉拒的渣男?明明刷了三个月题库,背熟八股文,结果一被问项目就支支吾吾,简历写得像大学生课程设计?...
- 谷歌云平台:开发者部署超120个开源包
-
从国外相关报道了解,Google与Bitnami合作为Google云平台增加了一个新的功能,为了方便开发人员快捷部署程序,提供了120余款开源应用程序云平台的支持。这些应用程序其中包括了WordPre...
- 知名互联网公司和程序员都看好的数据库是什么?
-
2017年数据库领域的最大趋势是什么?什么是最热的数据处理技术?学什么数据库最有前途?程序员们普遍不喜欢的数据库是什么?本文都会一一揭秘。大数据时代,数据库的选择备受关注,此前本号就曾揭秘国内知名互联...
- 腾讯云发布云存储MongoDB服务
-
近日,著名安全专家兼Shodan搜索引擎的创建者JohnMatherly发现,目前至少有35000个受影响的MongoDB数据库暴露在互联网上,它们所包含的数据暴露在网络攻击风险之中。据估计,将近6...
- 已跪,Java全能笔记爆火,分布式/开源框架/微服务/性能调优全有
-
前言程序员,立之根本还是技术,一个程序员的好坏,虽然不能完全用技术强弱来判断,但是技术水平一定是基础,技术差的程序员只能CRUD,技术不深的程序员也成不了架构师。程序员对于技术的掌握,除了从了解-熟悉...
- 面试官:举个你解决冲突的例子?别怂!用这个套路……
-
面试官:举个你解决冲突的例子?别怂!用这个套路……最近收到粉丝私信,说被问到:团队技术方案有分歧怎么办?当场大脑宕机……兄弟!这不是送命题,是展示你情商+技术判断力的王炸题!今天教你们3招,用真实案例...
- 面试碰到MongoDB?莫慌,跟面试官这样吹MongoDB 复制集
-
推荐阅读:吊打MySQL:21性能优化实践+学习导图+55面试+笔记+20高频知识点阿里一线架构师分享的技术图谱,进阶加薪全靠它十面字节跳动,依旧空手而归,我该放弃吗?文末会分享一些MongoDB的学...
- SpringBoot集成扩展-访问NoSQL数据库之Redis和MongoDB!
-
与关系型数据库一样,SpringBoot也提供了对NoSQL数据库的集成扩展,如对Redis和MongoDB等数据库的操作。通过默认配置即可使用RedisTemplate和MongoTemplate...
- Java程序员找工作总卡项目关?
-
Java程序员找工作总卡项目关?3招教你用真实经历写出HR抢着要的简历!各位Java老哥,最近刷招聘软件是不是手都划酸了?简历投出去石沉大海,面试邀请却总在飞别人的简历?上周有个兄弟,13年经验投了5...
- Java多租户SaaS系统实现方案
-
嗯,用户问的是Java通过租户id实现的SaaS方案。首先,我需要理解用户的需求。SaaS,也就是软件即服务,通常是指多租户的架构,每个租户的数据需要隔离。用户可能想知道如何在Java中利用租户ID来...
你 发表评论:
欢迎- 一周热门
- 最近发表
- 标签列表
-
- oracle位图索引 (63)
- oracle批量插入数据 (62)
- oracle事务隔离级别 (53)
- oracle 空为0 (50)
- oracle主从同步 (55)
- oracle 乐观锁 (51)
- 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)