【需求变更】使用 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 分钟前
java-集合
java·后端·程序员
真实的菜8 分钟前
消息队列高级特性与原理:解锁分布式系统的底层逻辑
java
若水不如远方9 分钟前
java范型
java
凌辰揽月12 分钟前
Web后端基础(基础知识)
java·开发语言·前端·数据库·学习·算法
lifallen18 分钟前
深入浅出 Arrays.sort(DualPivotQuicksort):如何结合快排、归并、堆排序和插入排序
java·开发语言·数据结构·算法·排序算法
长安不见20 分钟前
背景知识: 理解LimitLatch背后的AQS
java
小吕学编程22 分钟前
策略模式实战:Spring中动态选择商品处理策略的实现
java·开发语言·设计模式
weixin_4383354028 分钟前
Spring Boot实现接口时间戳鉴权
java·spring boot·后端
pan_junbiao1 小时前
Spring框架的设计模式
java·spring·设计模式
远方16091 小时前
0x-2-Oracle Linux 9上安装JDK配置环境变量
java·linux·oracle