wordpress dirname,网站标题优化,做海报有什么参考的网站,成都装修培训学校在 Redis 的 2.6 以上版本中#xff0c;除了可以使用命令外#xff0c;还可以使用 Lua 语言操作 Redis。从前面的命令可以看出 Redis 命令的计算能力并不算很强大#xff0c;而使用 Lua 语言则在很大程度上弥补了 Redis 的这个不足。
只是在 Redis 中#xff0c;执行 Lua …在 Redis 的 2.6 以上版本中除了可以使用命令外还可以使用 Lua 语言操作 Redis。从前面的命令可以看出 Redis 命令的计算能力并不算很强大而使用 Lua 语言则在很大程度上弥补了 Redis 的这个不足。
只是在 Redis 中执行 Lua 语言是原子性的也就说 Redis 执行 Lua 的时候是不会被中断的具备原子性这个特性有助于 Redis 对并发数据一致性的支持。
Redis 支持两种方法运行脚本一种是直接输入一些 Lua 语言的程序代码另外一种是将 Lua 语言编写成文件。
在实际应用中一些简单的脚本可以采取第一种方式对于有一定逻辑的一般采用第二种方式。而对于采用简单脚本的Redis 支持缓存脚本只是它会使用 SHA-1 算法对脚本进行签名然后把 SHA-1 标识返回回来只要通过这个标识运行就可以了。
执行输入 Lua 程序代码
它的命令格式为
eval lua-script key-num [key1 key2 key3 ...] [value1 value2 value3 ...]解说 eval 代表执行 Lua 语言的命令。Lua-script 代表 Lua 语言脚本。key-num 整数代表参数中有多少个 key需要注意的是 Redis 中 key 是从 1 开始的如果没有 key 的参数那么写 0。[key1key2key3...] 是 key 作为参数传递给 Lua 语言也可以不填它是 key 的参数但是需要和 key-num 的个数对应起来。[value1 value2 value3...] 这些参数传递给 Lua 语言它们是可填可不填的。这里难理解的是 key-num 的意义举例说明。
可以看到执行了两个 Lua 脚本。
eval returnhello java 0这个脚本只是返回一个字符串并不需要任何参数所以 key-num 填写了 0代表着没有任何 key 参数。按照脚本的结果就是返回了 hello java所以执行后 Redis 也是这样返回的。这个例子很简单只是返回一个字符串。
eval redis.call(set,KEYS[1],ARGV[1]) 1 lua-key lua-value设置一个键值对可以在 Lua 语言中采用 redis.call(command,key[param1,param2…]) 进行操作其中 command 是命令包括 set、get、del 等。Key 是被操作的键。param1,param2...代表给 key 的参数。脚本中的 KEYS[1] 代表读取传递给 Lua 脚本的第一个 key 参数而 ARGV[1] 代表第一个非 key 参数。
这里共有一个 key 参数所以填写的 key-num 为 1这样 Redis 就知道 key-value 是 key 参数而 lua-value 是其他参数它起到的是一种间隔的作用。
最后我们可以看到使用 get 命令获取数据是成功的所以 Lua 脚本运行成功了。
有时可能需要多次执行同样一段脚本这个时候可以使用 Redis 缓存脚本的功能在 Redis 中脚本会通过 SHA-1 签名算法加密脚本然后返回一个标识字符串可以通过这个字符串执行加密后的脚本。
这样的一个好处在于如果脚本很长从客户端传输可能需要很长的时间那么使用标识字符串则只需要传递 32 位字符串即可这样就能提高传输的效率从而提高性能。
首先使用命令
script load script这个脚本的返回值是一个 SHA-1 签名过后的标识字符串我们把它记为 shastring。通过 shastring 可以使用命令执行签名后的脚本命令的格式是
evalsha shastring keynum [key1 key2 key3 ...] [param1 param2 param3 ...]下面演示过程。
对脚本签名后就可以使用 SHA-1 签名标识运行脚本了。在 Spring 中演示这样的一个过程如果是简单存储笔者认为原来的 API 中的 Jedis 对象就简单些所以先获取了原来的 connection 对象代码如下所示。
// 如果是简单的对象使用原来的封装会简易些
ApplicationContext applicationContext new ClassPathXmlApplicationContext(applicationContext.xml);
RedisTemplate redisTemplate applicationContext.getBean(RedisTemplate.class);
applicationContext.getBean(RedisTemplate.class);
// 如果是简单的操作使用原来的Jedis会简易些
Jedis jedis (Jedis) redisTemplate.getConnectionFactory().getConnection().getNativeConnection();
// 执行简单的脚本
String helloJava (String) jedis.eval(return hello java);
System.out.println(helloJava);
// 执行带参数的脚本
jedis.eval(redis.call (set, KEYS [1],ARGV [1]), 1, lua-key,lua-value);
String luaKey (String) jedis.get(lua-key);
System.out.println(luaKey);
// 缓存脚本返回shal签名标识
String shal jedis.scriptLoad(redis.call(set,KEYS[1], ARGV[1]));
// 通过标识执行脚本
jedis.evalsha(shal, 1, new String[] { sha-key, sha-val });
// 获取执行脚本后的数据
String shaVal jedis.get(sha-key);
System.out.println(shaVal);
// 关闭连接
jedis.close();上面演示的是简单字符串的存储但现实中可能要存储对象这个时候可以考虑使用 Spring 提供的 RedisScript 接口它还是提供了一个实现类—— DefaultRedisScript让我们来了解它的使用方法。
这里先来定义一个可序列化的对象 Role因为要序列化所以需要实现 Serializable 接口代码如下所示。
public class Role implements Serializable {/*** 注意对象要可序列化需要实现Serializable接口往往要重写serialVersionUID*/private static final long serialVersionUID 3447499459461375642L;private long id;private String roleName;private String note;// 省略setter和getter
}这个时候就可以通过 Spring 提供的 DefaultRedisScript 对象执行 Lua 脚本来操作对象了代码如下所示。
ApplicationContext applicationContext new ClassPathXmlApplicationContext(applicationContext.xml);
RedisTemplate redisTemplate applicationContext.getBean(RedisTemplate.class);
// 定义默认脚本封装类
DefaultRedisScriptRole redisScript new DefaultRedisScriptRole();
// 设置脚本
redisScript.setScriptText(redis.call(set,KEYS[1], ARGV[1]) return redis.call(get, KEYS[1]));
// 定义操作的key列表
ListString keyList new ArrayListString();
keyList.add(role1);
// 需要序列化保存和读取的对象
Role role new Role();
role.setId(1L);
role.setRoleName(role_name_1);
role.setNote(note_1);
// 获得标识字符串
String sha1 redisScript.getSha1();
System.out.println(sha1);
// 设置返回结果类型如果没有这句话结果返回为空
redisScript.setResultType(Role.class);
// 定义序列化器
JdkSerializationRedisSerializer serializer new JdkSerializationRedisSerializer();
// 执行脚本
// 第一个是RedisScript接口对象第二个是参数序列化器
// 第三个是结果序列化器第四个是Reids的key列表最后是参数列表
Role obj (Role) redisTemplate.execute(redisScript, serializer,serializer, keyList, role);
// 打印结果
System.out.println(obj);注意加粗的代码两个序列化器第一个是参数序列化器第二个是结果序列化器。这里配置的是 Spring 提供的 JdkSerializationRedisSerializer如果在 Spring 配置文件中将 RedisTemplate 的 valueSerializer 属性设置为 JdkSerializationRedisSerializer那么使用默认的序列化器即可。
执行 Lua 文件
我们把 Lua 变为一个字符串传递给 Redis 执行而有些时候要直接执行 Lua 文件尤其是当 Lua 脚本存在较多逻辑的时候就很有必要单独编写一个独立的 Lua 文件。比如编写了一段 Lua 脚本代码如下所示。
redis.call(set,KEYS[1],ARGV[1])
redis.call(set,KEYS[2],ARGV[2])
local n1 tonumber(redis.call(get,KEYS[1]))
local n2 tonumber(redis.call(get,KEYS[2]))
if n1 n2 thenreturn 1
end
if n1 n2 thenreturn 0
end
if n1 n2 thenreturn 2
end这是一个可以输入两个键和两个数字记为 n1 和 n2的脚本其意义就是先按键保存两个数字然后去比较这两个数字的大小。当 n1n2 时就返回 0当 n1n2 时就返回 1当 n1n2 时就返回 2且把它以文件名 test.lua 保存起来。这个时候可以对其进行测试在 Windows 或者在 Linux 操作系统上执行下面的命令
redis-cli --eval test.lua key1 key2 , 2 4注意redis-cli 的命令需要注册环境或者把文件放置在正确的目录下才能正确执行这样就能看到效果如图所示。
看到结果就知道已经运行成功了。只是这里需要非常注意命令执行的命令键和参数是使用逗号分隔的而键之间用空格分开。在本例中 key2 和参数之间是用逗号分隔的而这个逗号前后的空格是不能省略的这是要非常注意的地方一旦左边的空格被省略了那么 Redis 就会认为“key2,”是一个键一旦右边的空格被省略了Redis 就会认为“,2”是一个键。
在 Java 中没有办法执行这样的文件脚本可以考虑使用 evalsha 命令这里更多的时候我们会考虑 evalsha 而不是 eval因为 evalsha 可以缓存脚本并返回 32 位 sha1 标识我们只需要传递这个标识和参数给 Redis 就可以了使得通过网络传递给 Redis 的信息较少从而提高了性能。
如果使用 eval 命令去执行文件里的字符串一旦文件很大那么就需要通过网络反复传递文件问题往往就出现在网络上而不是 Redis 的执行效率上了。参考上面的例子去执行下面我们模拟这样的一个过程使用 Java 执行 Redis 脚本代码如下所示。
public static void testLuaFile() {ApplicationContext applicationContext new ClassPathXmlApplicationContext(applicationContext.xml);RedisTemplate redisTemplate applicationContext.getBean(RedisTemplate.class);//读入文件流File file new File(G:\\dev\\redis\\test.lua);byte[] bytes getFileToByte(file);Jedis jedis (Jedis)redisTemplate.getConnectionFactory().getConnection().getNativeConnection();//发送文件二进制给Redis,这样REdis就会返回shal标识byte[] shal jedis.scriptLoad(bytes);//使用返回的标识执行其中第二个参数2,表示使用2个键//而后面的字符串都转化为了二进制字节进行传输Object obj jedis.evalsha(shal,2, key1.getBytes(),key2.getBytes(),2.getBytes(), 4.getBytes());System.out.println(obj);
}
/**
* 把文件转化为二进制数组
* param file 文件
* return二进制数组
*/
public static byte[] getFileToByte(File file) {byte[] by new byte[(int) file.length()];try {InputStream is new FileinputStream(file);ByteArrayOutputStream bytestream new ByteArrayOutputStream(); byte[] bb new byte[2048];int ch;ch is.read(bb);while (ch ! -1) {bytestream.write(bb, 0, ch);ch is.read(bb);}by bytestream.toByteArray();} catch (Exception ex) {ex.printStackTrace();}return by;
}如果我们将 sha1 这个二进制标识保存下来那么可以通过这个标识反复执行脚本只需要传递 32 位标识和参数即可无需多次传递脚本。
从对 Redis 的流水线的分析可知系统性能不佳的问题往往并非是 Redis 服务器的处理能力更多的是网络传递因此传递更少的内容有利于系统性能的提高。
这里采用比较原始的 Java Redis 连接操作 Redis还可以采用 Spring 提供的 RedisScript 操作文件这样就可以通过序列化器直接操作对象了。