通过java客户端实现上述功能有一定的缺陷,在高并发的情况下,数据可能有不一致的情况;建议使用lua脚本封装整个逻辑,保证操作的原子性;可以通过SCRIPT LOAD的方式将脚本缓存到服务器,通过sha1校验值+参数(Key,ARG)来执行,减轻网络传输;
给你个示例:
1.功能需求:key如果不存在,则set并返回false,并设置超时时间和count=1;达到超时时间或者达到指定的count数,返回false 并重新设置超时时间和count=1;如果没有达到超时时间 & 没有达到指定的count,返回true,并incr count
redis.lua脚本如下:
local key = KEYS[1];
local expire = tonumber(KEYS[2]);
local number = tonumber(KEYS[3]);
local count = tonumber(redis.call("GET",key));
if count == nil then
redis.call("SETEX",KEYS[1],expire,"1");
return false;
else
if count +1 >= number then;
redis.call("SETEX",KEYS[1],expire,"1");
return false;
else
redis.call("INCR",KEYS[1]);
return true;
end
end
java代码如下:
public static final String REDIS_LUA = "redis.call('select',1);local key = KEYS[1];local expire = tonumber(KEYS[2]);" +
"local number = tonumber(KEYS[3]);local count = tonumber(redis.call('GET',key));" +
"if count == nil then redis.call('SETEX',KEYS[1],expire,'1');return 0;" +
"else if count +1 >= number then redis.call('SETEX',KEYS[1],expire,'1');return 0;" +
"else redis.call('INCR',KEYS[1]);return 1;end;end;";
private static final String redisScript;
static {
try {
Conf.load();
} catch (Exception e) {
e.printStackTrace();
}
jedisPool = new JedisPool(Conf.getRedisHost(), Conf.getRedisPort());
redisScript = loadLuaScript();
}
private static String loadLuaScript() {
Jedis jedis = null;
String redisScript = null;
try {
jedis = jedisPool.getResource();
redisScript = jedis.scriptLoad(REDIS_LUA);
} catch (Exception e) {
if (jedis != null) {
jedisPool.returnResource(jedis);
}
} finally {
if (jedis != null) {
jedisPool.returnResource(jedis);
}
}
return redisScript;
}
调用脚本
Object result = jedis.evalsha(redisScript, ARGS_LENGTH
, key, expireTime, countVal);