redis实现 msetex和 getdel命令

Posted zslm___

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了redis实现 msetex和 getdel命令相关的知识,希望对你有一定的参考价值。

1.redis本身不提供 msetex命令(批量增加key并设置过期时间)

class RedisExtend {
    private static final Logger logger = LoggerFactory.getLogger(RedisExtend.class);
    private static final int Port = 6379;
    private static final String Host = "192.168.1.1";
    private static final String PASS_WORD = "1234";
    private static Jedis instance;
    /**
     * Lua脚本(msetex)
     */
    private static final String LUA_SCRIPT_MSETEX = "local keysLen = table.getn(KEYS);" +
            "local argvLen = table.getn(ARGV);" +
            "local idx=1;" +
            "local argVIdx=1;" +
            "for idx=1,keysLen,1 do " +
            "argVIdx=(idx-1)*2+1; " +
            "redis.call(‘Set‘,KEYS[idx],ARGV[argVIdx],‘EX‘,ARGV[argVIdx+1]);" +
            "end " +
            "return keysLen;";

    private static String LUA_SCRIPT_MSETEX_SHA1;

    /**
     * Lua脚本 (获取后删除)
     */
    private static final String LUA_SCRIPT_GET_AND_DELETE =
            "local current = redis.call(‘get‘, KEYS[1]);
" +
                    "if (current) then
" +
                    "    redis.call(‘del‘, KEYS[1]);
" +
                    "end
" +
                    "return current;";
    private static String LUA_SCRIPT_GET_AND_DELETE_SHA1;

    static {
        LUA_SCRIPT_MSETEX_SHA1 = SHA1.encode(LUA_SCRIPT_MSETEX);
        LUA_SCRIPT_GET_AND_DELETE_SHA1 = SHA1.encode(LUA_SCRIPT_GET_AND_DELETE);
    }

    public static Jedis getInstance() {
        if (instance == null) {
            instance = initJedisLite().getTemplate().getJedisPool().getResource();
        }
        return instance;
    }


    private static JedisLite initJedisLite() {
        return new JedisLite(Host, Port);
    }

    private static JedisTemplate jedisTemplate = initJedisLite().getTemplate();
 

    public static long msetex(List<RedisKeyValue> redisKeyValues) {
        if (CollectionUtils.isEmpty(redisKeyValues)) {
            return 0;
        }

        int keyCount = redisKeyValues.size();
        List<String> param = new ArrayList<>(keyCount * 3);
        for (RedisKeyValue item : redisKeyValues) {
            Assert.notNull(item, "KeyValue不允许为空");
            Assert.hasLength(item.getKey(), "Key不允许为空");
            param.add(item.getKey());
        }

        for (RedisKeyValue item : redisKeyValues) {
            param.add(item.getValue());
            param.add(Integer.toString(item.getSeconds()));
        }
        String[] paramArr = new String[param.size()];
        param.toArray(paramArr);
        return execLunaScript(new RedisScript(LUA_SCRIPT_MSETEX, LUA_SCRIPT_MSETEX_SHA1), keyCount, paramArr,
                (o) -> (long) (o == null ? 0L : Integer.parseInt(o.toString())));
    }

    public static String getdel(String key) {
        return execLunaScript(new RedisScript(LUA_SCRIPT_GET_AND_DELETE, LUA_SCRIPT_GET_AND_DELETE_SHA1), 1, new String[]{key},
                (o) -> (o == null ? null : o.toString()));
    }

    private static <T> T execLunaScript(RedisScript redisScriptObj, int keyCount, String[] param,
            Function<Object, T> function) {
        try {
            return jedisTemplate.execute((Jedis jedis) -> function.apply(jedis.evalsha(redisScriptObj.sha1, keyCount,
                    param)));
        } catch (redis.clients.jedis.exceptions.JedisNoScriptException ex) {
            return jedisTemplate.execute((Jedis jedis) -> function.apply(jedis.eval(redisScriptObj.script, keyCount,
                    param)));
        } catch (Exception ex) {
            logger.error("执行redis脚本异常!", ex);
            return null;
        }
    }
 
    static class RedisScript {
        private String script;
        private String sha1;

        public RedisScript(String script) {
            this(script, SHA1.encode(script));
        }

        public RedisScript(String script, String sha1) {
            this.script = script;
            this.sha1 = sha1;
        }
    }

    static class RedisKeyValue {
        private String key;
        private String value;
        private int seconds;

        public RedisKeyValue(String key, String value) {
            this(key, value, -1);
        }

        public RedisKeyValue(String key, String value, int seconds) {
            this.key = key;
            this.value = value;
            this.seconds = seconds;
        }

        public String getKey() {
            return key;
        }

        public String getValue() {
            return value;
        }

        public int getSeconds() {
            return seconds;
        }
    }
}

 

2.调用代码如下

 

    public static void main(String[] args) {
        long r = msetex(
                Arrays.asList(new RedisKeyValue("key1", "value1", 5000),
                        new RedisKeyValue("key2", "value2", 5000),
                        new RedisKeyValue("ke3", "value3", 5000)
                ));
        System.out.println("返回值:" + r);

        String key1Val = getdel("key1");
        System.out.println("key1的值:" + key1Val);
        key1Val = getdel("key1");
        System.out.println("key1的值:" + key1Val);
    }

 

3.返回结果如下

返回值:3
key1的值:value1
key1的值:null

 

 

4.sha1的代码如下

import java.security.MessageDigest;


public class SHA1 {
    private static final char[] HEX_DIGITS = {‘0‘, ‘1‘, ‘2‘, ‘3‘, ‘4‘, ‘5‘,
            ‘6‘, ‘7‘, ‘8‘, ‘9‘, ‘a‘, ‘b‘, ‘c‘, ‘d‘, ‘e‘, ‘f‘};

    /**
     * Takes the raw bytes from the digest and formats them correct.
     *
     * @param bytes the raw bytes from the digest.
     * @return the formatted bytes.
     */
    private static String getFormattedText(byte[] bytes) {
        int len = bytes.length;
        StringBuilder buf = new StringBuilder(len * 2);
        // 把密文转换成十六进制的字符串形式
        for (int j = 0; j < len; j++) {
            buf.append(HEX_DIGITS[(bytes[j] >> 4) & 0x0f]);
            buf.append(HEX_DIGITS[bytes[j] & 0x0f]);
        }
        return buf.toString();
    }

    public static String encode(String str) {
        if (str == null) {
            return null;
        }
        try {
            MessageDigest messageDigest = MessageDigest.getInstance("SHA1");
            messageDigest.update(str.getBytes());
            return getFormattedText(messageDigest.digest());
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }
}

 

以上是关于redis实现 msetex和 getdel命令的主要内容,如果未能解决你的问题,请参考以下文章

那些问哭你的Redis分布式锁

redis集群代建

面试:那些问哭你的Redis分布式锁!

访问func委托中的闭包变量c#

面试被问Redis锁的缺点,被打击的扎心了

Redis 命令执行过程(下)