文章目录

  • 一、原子性
  • 二、Lua
  • 2.1 Lua脚本示例
  • 2.2 redis-cli使用Lua脚本
  • 2.3 jedis使用Lua脚本
  • 三、参考


一、原子性

  • 原子性是指不可打断的一个完整操作。redis中单个命令是原子的,但是如果期望多个命令组合成一个原子操作,就不行
    了,此时需要借助Lua脚本来实现原子性、
  • Redis中的弱事务。redis的简单事务,将一组需要一起执行的命令放到multi和exec两个命令之间,其中multi代表事务开始,
    exec代表事务结束,不支持事物回滚,不能提供完整的事物特性,使用较少,涉及到的指令如下。
multi、watch、discard、exec

二、Lua

  • Lua是一个小巧的脚本语言,由标准C编写而成,代码简洁优美,几乎在所有操作系统和平台上都可以编译,运行。
    一个完整的Lua解释器不过200k,在目前所有脚本引擎中,Lua的速度是最快的。Lua内的代码可以保证原子性,要么全部成功,要么全部失败。
  • 在redis中使用Lua脚本的好处:
1.减少网络开销
2.原子操作
3.复用性

2.1 Lua脚本示例

  • 如下脚本就是简单的保存address=shenzhen这个键值对,保存成功就返回success,反之返回fail
if redis.call('set',"address","shenzhen") ~= "shenzhen" then
	return "success";
else
	return "fail";
end
  • 使用./redis-cli --eval ./01.lua 执行后发现数据库中保存键值对成功
  • 下面是一个传参数的lua脚本的调用方式
if redis.call('set',KEYS[1],KEYS[2]) ~= KEYS[1] then
	return "success";
else
	return "fail";
end
  • 使用./redis-cli --eval ./02.lua hobby basketball 执行后数据库中保存键值对hobby=basketball成功。
    其实传参方式很简单,命令后面跟着参数,脚本里面使用KEYS[n]来接受对应的参数,n>0

2.2 redis-cli使用Lua脚本

  • redis客户端执行无参lua脚本:./redis-cli -a 123456 --eval ./01.lua
  • redis客户端执行有参lua脚本:./redis-cli -a 123456 --eval ./01.lua param1 param2 param3 …

2.3 jedis使用Lua脚本

为什么Redis Cluster 下 Lua 脚本的原子操作也无法保证了 redis lua原子性_Lua

  • Redis代码调用Lua脚本使用eval方法,我们可以看到jedis中有很多重载方法,可以使用字符串和byte数组形式表示Lua和参数,可以批量传参。
public class MyLua {

    public static final String REDIS_HOST = "127.0.0.1";
    public static final String REDIS_PASSWORD = "Intellifusion@20190108";
    public static final int REDIS_PORT = 6379;
    public static Jedis jedis = new Jedis(REDIS_HOST);

//    static {
//        jedis.auth(REDIS_PASSWORD);
//    }


    public static void main(String[] args) throws InterruptedException, IOException {
        byte[] lua = getContent("./02.lua");
        //key的字节数组集合
        List<byte[]> keyList = new ArrayList<>();
        keyList.add("hobby".getBytes());
        //值的字节数组集合
        List<byte[]> valList = new ArrayList<>();
        keyList.add("code".getBytes());
        jedis.eval(lua, keyList, valList);
    }

    //读取文件并以byte数组形式返回
    public static byte[] getContent(String filePath) throws IOException {
        File file = new File(filePath);
        long fileSize = file.length();
        if (fileSize > Integer.MAX_VALUE) {
            System.out.println("file too big...");
            return null;
        }
        FileInputStream fi = new FileInputStream(file);
        byte[] buffer = new byte[(int) fileSize];
        int offset = 0;
        int numRead = 0;
        while (offset < buffer.length && (numRead = fi.read(buffer, offset, buffer.length - offset)) >= 0) {
            offset += numRead;
        }
        // 确保所有数据均被读取
        if (offset != buffer.length) {
            throw new IOException("Could not completely read file " + file.getName());
        }
        fi.close();
        return buffer;
    }
}
  • 执行后redis中hobby对应的值就被改变了

三、参考