【需求变更】使用 Redis 和 Lua 脚本实现变更后方案编号的生成

项目有部分需求变更,比如之前使用的 MyBatis-plus 封装的雪花算法,但现在一些业务比如某些方案编号,需要以特定格式生成,现在文件编号要求以 年-月-4位序号 的格式生成,当年或月变化时,4位序号需要重置,比如:2024-11-0001

这种需求类似于生成全局唯一 ID,并且要求自增,Redis 似乎非常合适,准备开干💪

  1. Redis 中 String 非常适合做计数器 ,因为我需要保存的 value 是整数值并且这个整数值可以用 long 类型来表示,Redis 底层会将整数值保存在字符串对象结构的 ptr属性里面,并将字符串对象的编码设置为 int

    除此之外,String 还可以保存一些常用的对象 JSON 数据,缓存 Session 信息,实现分布式锁等。

  2. 在项目中 Redis 中存储了大量经常查询的数据,为减少 Redis 的内存压力,设置过期时间为一个月,同时为保证原子性和并发安全,使用 lua 脚本实现 redis 操作。

java 复制代码
  /**
  *	初始化方案编号
  */
  private static final String LUA_GENERATE_PROJECT_CODE = "lua/generate_project_code.lua";
  
  @Resource
  private StringRedisTemplate stringRedisTemplate;
  
  private void initBaseInfo(ProjectVo vo) {
      if (ObjUtils.isEmpty(vo.getProjectCode())) {
          String operateDate = DateUtils.getCurDate();
          String[] parts = operateDate.split("-");
          String year = parts[0];
          String month = parts[1];
          String prefix = "PROJECT:PROJECT_CODE:";
          String key = prefix + year + ":" + month + ":";
          DefaultRedisScript<String> defaultRedisScript = new DefaultRedisScript<>();
          defaultRedisScript.setResultType(String.class);
		  defaultRedisScript.setScriptSource(new ResourceScriptSource(new ClassPathResource(LUA_GENERATE_PROJECT_CODE)));
          String sequence = stringRedisTemplate.execute(defaultRedisScript, Collections.singletonList(key));
          String projectCode = year + "-" + month + "-" + sequence;
          vo.setProjectCode(projectCode);
      }
  }
lua 复制代码
	--如果根据 key 获取 value == nil,那么就说明到了下个月,重置 计数器
	--否则 计数器++,返回值是格式化为四位的字符串
	local key = KEYS[1]
	local current = redis.call("get", key)
	if current == nil then
	  -- 设置过期时间 31 天
	  redis.call("set", key, "1", "EX", 2678400)
	  current = "1"
	else
	  redis.call("incr", key)
	  current = redis.call("get", key)
	end
	local value = tonumber(current)
	return string.format("%04d", value)

需要注意即使设置过期时间为 31 天,由于当月或年发生变化,所以 key 不存在,会直接创建 key,并重置计数器的值为 1。

  • 在实现需求过程遇到的问题
    • 在项目中已经封装了一个 Redis 工具类,但功能较少,且使用的是 RedisTemplate 对象,我直接使用工具类中的对象生成时报了 序列化反序列化异常 ,还好之前学习 Redis 时,有记录一些笔记和思考,这是因为 Redis 默认使用 JDK 方式的序列化,实际上 Redis 已经帮助我们简化了序列化和反序列化的过程,可以直接注入 StringRedisTemplate,具体可参考之前的文章。 RedisTemplate的默认序列化方式及改进【一】

      RedisTemplate的默认序列化方式及改进【二】

相关推荐
即将进化成人机4 分钟前
springboot项目创建方式
java·spring boot·后端
教练、我想打篮球12 分钟前
117 javaweb servlet+jsp 项目中修改了 数据库连接配置, 却怎么都不生效
java·servlet·jdbc·jsp
你不是我我14 分钟前
【Java 开发日记】我们来说一说 Redis IO 多路复用模型
java·开发语言·redis
SadSunset16 分钟前
(13)复杂查询
java·笔记·架构·mybatis
浩瀚地学17 分钟前
【Java】ArrayList
java·开发语言·经验分享·笔记
阿杰同学25 分钟前
Java 设计模式 面试题及答案整理,最新面试题
java·开发语言·设计模式
这样の我25 分钟前
java 模拟chrome指纹 处理tls extension顺序
java·开发语言·chrome
Genevieve_xiao30 分钟前
【数据结构与算法】【xjtuse】面向考纲学习(下)
java·数据结构·学习·算法
4311媒体网32 分钟前
php和c++哪个更好学?C++难学吗?
java·c++·php
毕设源码-朱学姐37 分钟前
【开题答辩全过程】以 基于SpringBoot的流行音乐网站的设计与实现为例,包含答辩的问题和答案
java·spring boot·后端