使用原子自增和 expire 搭配实现原子操作
@Bean(name = "customStringRedisTemplate") public RedisTemplate<String, String> customStringRedisTemplate(RedisConnectionFactory factory) { // 要求使用默认序列化,否则有可能会抛出user_script:1: ERR value is not an integer or out of range 异常 return new StringRedisTemplate(factory); } @Autowired private RedisTemplate<String, String> customStringRedisTemplate; @Test public void customLock() { String key = "cmq_im_dispatch_user_"; // customStringRedisTemplate.delete(key); String incrByAndExpireLua = "if redis.call(‘incrBy‘, KEYS[1], ARGV[1]) == tonumber(ARGV[1]) then\n" + " redis.call(‘expire‘, KEYS[1], ARGV[2])\n" + " return tonumber(ARGV[1])\n" + "else\n" + " return 0\n" + "end"; if (!customStringRedisTemplate.hasKey(key)) { DefaultRedisScript<Long> redisScript = new DefaultRedisScript<>(incrByAndExpireLua, Long.class);
// 要求这里输入的参数都需要为字符串 Long increment = customStringRedisTemplate.execute(redisScript, Collections.singletonList(key), NumberUtils.INTEGER_ONE.toString(), "300"); // 即使在并发情况下也只允许一个线程进入 if (NumberUtils.LONG_ONE.equals(increment)) { try { log.info("{}线程进入", Thread.currentThread().getName()); } finally { customStringRedisTemplate.delete(key); } } } }
对于lua脚本的分析:
java - redis - incr and expire lua
原文:https://www.cnblogs.com/xingguoblog/p/13738907.html