【需求变更】使用 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的默认序列化方式及改进【二】

相关推荐
卓怡学长6 小时前
m111基于MVC的舞蹈网站的设计与实现
java·前端·数据库·spring boot·spring·mvc
存在的五月雨6 小时前
Redis的一些使用
java·数据库·redis
Elias不吃糖13 小时前
Java Lambda 表达式
java·开发语言·学习
情缘晓梦.13 小时前
C语言指针进阶
java·开发语言·算法
鲨莎分不晴13 小时前
Redis 基本指令与命令详解
数据库·redis·缓存
南知意-15 小时前
IDEA 2025.3 版本安装指南(完整图文教程)
java·intellij-idea·开发工具·idea安装
码农水水15 小时前
蚂蚁Java面试被问:混沌工程在分布式系统中的应用
java·linux·开发语言·面试·职场和发展·php
海边的Kurisu16 小时前
苍穹外卖日记 | Day4 套餐模块
java·苍穹外卖
毕设源码-邱学长16 小时前
【开题答辩全过程】以 走失儿童寻找平台为例,包含答辩的问题和答案
java
他们叫我技术总监16 小时前
Python 列表、集合、字典核心区别
android·java·python