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

相关推荐
pshdhx_albert3 小时前
AI agent实现打字机效果
java·http·ai编程
沉鱼.444 小时前
第十二届题目
java·前端·算法
一个有温度的技术博主4 小时前
Redis主从同步原理:从全量同步到增量同步的完整解析
redis·分布式·缓存
赫瑞4 小时前
数据结构中的排列组合 —— Java实现
java·开发语言·数据结构
小红的布丁5 小时前
单线程 Redis 的高性能之道
redis·后端
周末也要写八哥6 小时前
多进程和多线程的特点和区别
java·开发语言·jvm
惜茶6 小时前
vue+SpringBoot(前后端交互)
java·vue.js·spring boot
杰克尼7 小时前
springCloud_day07(MQ高级)
java·spring·spring cloud
NHuan^_^9 小时前
SpringBoot3 整合 SpringAI 实现ai助手(记忆)
java·人工智能·spring boot
Mr_Xuhhh9 小时前
从ArrayList到LinkedList:理解链表,掌握Java集合的另一种选择
java·数据结构·链表