在Redis的2.6以上版本中,除了可以使用命令外,还可以使用Lua语言操作Redis。
Redis支持两种方式运行脚本,一种是直接输入一些Lua语言的程序代码;另外一种使将Lua语言编写成文件。
初步认识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.
- [key1,key2,key3 …]:key作为参数传递给Lua语言,也可以不填,但是需要和key-num的个数对应起来。
- [value1 value2 value3 …]:作为参数传递给Lua语言,它们也是可填可不填的。
这里我们看两个Lua脚本:
eval "return 'Hello World!'" 0
这个脚本只是返回一个字符串,并不需要任何参数,所以key-num填写了0,代表着没有任何key参数,执行结果就是返回 Hello World!。
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的参数。
有时候可能需要多次执行同样一段脚本,这个时候可以使用Redis缓存脚本的功能,在Redis中脚本会通过SHA-1签名算法加密脚本,然后返回一个标识字符串,可以通过这个字符串执行加密后的脚本。
首先使用命令:
script load script
这个脚本的返回值是一个SHA-1签名过后的标识字符串,我们把它记为shastring。通过shastring可以使用命令执行签名后的脚本,命令的格式是:
evalsha shastring keynum [key1 key2 key3 ...] [param1 param2 param3 ...]
下面我们在Spring中演示这个过程。
这里我们先定义一个可序列化的对象Role,因为要序列化所以需要实现Serializable接口,代码如下:
package com.ssm.lua.pojo;
import java.io.Serializable;
public class Role implements Serializable {
private static final long serialVersionUID=7247714666080613254L;
private Long id;
private String roleName;
private String note;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getRoleName() {
return roleName;
}
public void setRoleName(String roleName) {
this.roleName = roleName;
}
public String getNote() {
return note;
}
public void setNote(String note) {
this.note = note;
}
}
配置文件:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="poolConfig" class="redis.clients.jedis.JedisPoolConfig">
<property name="maxIdle" value="50" />
<property name="maxTotal" value="100" />
<property name="maxWaitMillis" value="20000" />
</bean>
<bean id="stringRedisSerializer" class="org.springframework.data.redis.serializer.StringRedisSerializer" />
<bean id="connectionFactory" class="org.springframework.data.redis.connection.jedis.JedisConnectionFactory">
<property name="hostName" value="localhost" />
<property name="port" value="6379" />
<property name="password" value="123456" />
<property name="poolConfig" ref="poolConfig" />
</bean>
<bean id="redisTemplate" class="org.springframework.data.redis.core.RedisTemplate">
<property name="connectionFactory" ref="connectionFactory" />
<property name="defaultSerializer" ref="stringRedisSerializer" />
<property name="keySerializer" ref="stringRedisSerializer" />
<property name="valueSerializer" ref="stringRedisSerializer" />
</bean>
</beans>
这个时候就可以通过Spring提供的DefaultRedisScript对象执行Lua脚本来操作对象,代码如下:
package com.ssm.lua.test;
import com.ssm.lua.pojo.Role;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.data.redis.serializer.JdkSerializationRedisSerializer;
import java.util.ArrayList;
import java.util.List;
public class testLua {
public static void main(String[] args){
ApplicationContext applicationContext=new ClassPathXmlApplicationContext("redisSpring-cfg.xml");
RedisTemplate redisTemplate=applicationContext.getBean(RedisTemplate.class);
//定义默认脚本封装类
DefaultRedisScript<Role> redisScript=new DefaultRedisScript<>();
//设置脚本
redisScript.setScriptText("redis.call('set',KEYS[1],ARGV[1]) return redis.call('get',KEYS[1])");
//定义操作的key列表
List<String> keyList=new ArrayList<>();
keyList.add("role1");
//需要序列化保存和读取的对象
Role role=new Role();
role.setId(1L);
role.setRoleName("role_name_1");
role.setNote("role_note_1");
//获取标识字符串
String shastring=redisScript.getSha1();
System.out.println(shastring);
//设置返回结果的类型,如果没有这句话,结果返回为空。
redisScript.setResultType(Role.class);
//定义序列化器
JdkSerializationRedisSerializer serializer=new JdkSerializationRedisSerializer();
//执行脚本
//第一个是RedisScript接口对象,第二个是参数序列化器
//第三个是结果序列化器,第四个是Redis的key列表,最后是参数列表
Role obj=(Role)redisTemplate.execute(redisScript,serializer,serializer,keyList,role);
//打印结果
System.out.println(obj);
}
}
执行Lua文件
在上面我们把Lua变成一个字符串传递给Redis执行,而有些时候要直接执行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 then
return 1
end
if n1==n2 then
return 0
end
if n1<n2 then
return 2
end
这是一个可以输入两个键和两个数字(记为n1和n2)的脚本,其意义就是先按键保存两个数字,然后去比较这两个数字的大小。且把它以文件名test.lua保存起来。这个时候可以对其进行测试。
redis-cli --eval test.lua key1 key2 , 2 4
这里需要非常注意命令,执行的命令键和参数是要使用逗号隔开的,而键之间用空格分开。而上面的逗号前后的空格是不能省略的,这是要非常注意的地方。