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

相关推荐
沙子迷了蜗牛眼22 分钟前
当展示列表使用 URL.createObjectURL 的创建临时图片、视频无法加载问题
java·前端·javascript·vue.js
ganshenml24 分钟前
【Android】 开发四角版本全解析:AS、AGP、Gradle 与 JDK 的配套关系
android·java·开发语言
我命由我1234524 分钟前
Kotlin 运算符 - == 运算符与 === 运算符
android·java·开发语言·java-ee·kotlin·android studio·android-studio
musenh28 分钟前
redis和jedis
数据库·redis·缓存
小途软件31 分钟前
ssm327校园二手交易平台的设计与实现+vue
java·人工智能·pytorch·python·深度学习·语言模型
alonewolf_9935 分钟前
Java类加载机制深度解析:从双亲委派到热加载实战
java·开发语言
追梦者12336 分钟前
springboot整合minio
java·spring boot·后端
云游39 分钟前
Jaspersoft Studio community edition 7.0.3的应用
java·报表
帅气的你44 分钟前
Spring Boot 集成 AOP 实现日志记录与接口权限校验
java·spring boot
zhglhy1 小时前
Spring Data Slice使用指南
java·spring