SpringBoot Redis实现线程安全的最大值n自增后重置功能
解决方案
要实现Redis中key值自增到最大值n后重置,同时保证线程安全,核心是利用Redis的原子操作——因为Redis是单线程执行命令,Lua脚本可以确保整个逻辑的原子性,避免并发场景下的竞态问题。
实现步骤
1. 编写Lua脚本
Lua脚本会在Redis中原子执行,逻辑如下:
- 对指定key执行自增操作
- 判断自增后的值是否超过最大值
max - 若超过则将key重置为1,返回1;否则返回自增后的值
local key = KEYS[1] local max = tonumber(ARGV[1]) local current = redis.call('INCR', key) if current > max then redis.call('SET', key, 1) return 1 end return current
2. Java代码集成
通过RedisTemplate执行上述Lua脚本,确保操作的原子性:
@Autowired private RedisTemplate<String, Object> redisTemplate; // 预定义Lua脚本,避免重复编译 private static final String INCR_RESET_SCRIPT = "local key = KEYS[1]\n" + "local max = tonumber(ARGV[1])\n" + "local current = redis.call('INCR', key)\n" + "if current > max then\n" + " redis.call('SET', key, 1)\n" + " return 1\n" + "end\n" + "return current"; public long saveAndGetNextValue(long max) { DefaultRedisScript<Long> script = new DefaultRedisScript<>(); script.setScriptText(INCR_RESET_SCRIPT); script.setResultType(Long.class); // 执行脚本:传入key列表和max参数 return redisTemplate.execute(script, Collections.singletonList("myKey:"), max); }
3. 注意事项
- RedisTemplate序列化配置:确保key的序列化器为
StringRedisSerializer,避免key出现乱码导致脚本无法识别:@Bean public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) { RedisTemplate<String, Object> template = new RedisTemplate<>(); template.setConnectionFactory(factory); template.setKeySerializer(new StringRedisSerializer()); template.setHashKeySerializer(new StringRedisSerializer()); template.setValueSerializer(new Jackson2JsonRedisSerializer<>(Object.class)); template.setHashValueSerializer(new Jackson2JsonRedisSerializer<>(Object.class)); template.afterPropertiesSet(); return template; } - 最大值
max的取值:需传入正整数,若key不存在,首次自增会将其设为1,符合重置后的初始值逻辑。
为什么线程安全?
Redis执行Lua脚本时会阻塞其他命令,整个脚本的执行过程是原子的,不会被并发请求打断,因此完全避免了多线程场景下的竞态条件问题。
内容的提问来源于stack exchange,提问作者Bopanna
相关产品推荐
相关产品推荐

