lua redis执行lua脚本
???????????
redis使用命令行、redis-cli客户端、redisTemplate执行lua脚本
???????
??????????????
????????????????????????????????????
命令行、客户端执行脚本
?????????
语法格式
# 直接执行脚本(redis-cli客户端执行)
eval lua-script key-num [key key2 key3 ...] [value value2 value3 ...]
# 缓存脚本后执行(命令行执行,没有进入客户端)
redis-cli --eval --lua-script [key key2 ...] , [value value2 ...]
* key与value用逗号间隔,且逗号前后有空格)
??????????
lua 调用redis命令
redis.call(command, KEYS[1], KEYS[2], ... , ARGV[1], ARGV[2], ...)
# 命令说明
KEYS[1]:第一个key
ARGV[1]:第一个value
KEYS、ARGV不需要一一对应,个数不需要相等
# 示例
redis.call('set', KEYS[1], ARGV[1])
redis.call('get', KEYS[1])
??????????
示例:redis-cli客户端执行脚本
huli@hudeMacBook-Pro ~ % docker exec -it redis2 bash
-- redis-cli客户端
root@0d79dbff702e:/data# redis-cli
-- KEYS、ARGV名称是固定的,不能随便设置
127.0.0.1:6379> eval "redis.call('set',key[1],arg[1])" 1 set 瓜田李下
(error) ERR Error running script (call to f_c7f25ef16dd21c6a4dc44cf3964041227459f25e): @enable_strict_lua:15: user_script:1: Script attempted to access nonexistent global variable 'key'
-- set key=瓜田李下
127.0.0.1:6379> eval "redis.call('set',KEYS[1],ARGV[1])" 1 key 瓜田李下
(nil)
-- 获取key:没有return,无返回值
127.0.0.1:6379> eval "redis.call('get',KEYS[1])" 1 key
(nil)
-- 调用return,返回key对用的value(瓜田李下)
-- 由于是中文,redis存储的时候进行了序列化
127.0.0.1:6379> eval "return redis.call('get',KEYS[1])" 1 key
"\xe7\x93\x9c\xe7\x94\xb0\xe6\x9d\x8e\xe4\xb8\x8b"
-- 设置key2=gtlx
127.0.0.1:6379> eval "redis.call('set',KEYS[1],ARGV[1])" 1 key2 gtlx
(nil)
-- 返回key2对应的value(瓜田李下)
127.0.0.1:6379> eval "return redis.call('get',KEYS[1])" 1 key2
"gtlx"
???????????
示例:命令行执行脚本文件
huli@hudeMacBook-Pro ~ % docker exec -it redis2 bash
-- redis命令行生成脚本文件:test.lua
root@0d79dbff702e:/data# echo "redis.call('set', KEYS[1], ARGV[1]) return redis.call('get',KEYS[1])" > test.lua
-- 查看文件
root@0d79dbff702e:/data# ls
dump.rdb test.lua
root@0d79dbff702e:/data# cat test.lua
redis.call('set', KEYS[1], ARGV[1]) return redis.call('get',KEYS[1])
-- 命令行执行文件
root@0d79dbff702e:/data# redis-cli --eval test.lua key , gtlx
"gtlx"
??????????
????????????
????????????????????????????????????
redisTemplate 执行脚本
?????????
***********
相关类与接口
???????
RedisScript
public interface RedisScript<T> {
String getSha1();
@Nullable
Class<T> getResultType();
String getScriptAsString();
default boolean returnsRawValue() {
return this.getResultType() == null;
}
static <T> RedisScript<T> of(String script) {
return new DefaultRedisScript(script);
}
static <T> RedisScript<T> of(String script, Class<T> resultType) {
Assert.notNull(script, "Script must not be null!");
Assert.notNull(resultType, "ResultType must not be null!");
return new DefaultRedisScript(script, resultType);
}
static <T> RedisScript<T> of(Resource resource) {
Assert.notNull(resource, "Resource must not be null!");
DefaultRedisScript<T> script = new DefaultRedisScript();
script.setLocation(resource);
return script;
}
static <T> RedisScript<T> of(Resource resource, Class<T> resultType) {
Assert.notNull(resource, "Resource must not be null!");
Assert.notNull(resultType, "ResultType must not be null!");
DefaultRedisScript<T> script = new DefaultRedisScript();
script.setResultType(resultType);
script.setLocation(resource);
return script;
}
}
???????
DefaultRedisScript
public class DefaultRedisScript<T> implements RedisScript<T>, InitializingBean {
private final Object shaModifiedMonitor;
@Nullable
private ScriptSource scriptSource;
@Nullable
private String sha1;
@Nullable
private Class<T> resultType;
public DefaultRedisScript() {
this.shaModifiedMonitor = new Object();
}
public DefaultRedisScript(String script) {
this(script, (Class)null);
}
public DefaultRedisScript(String script, @Nullable Class<T> resultType) {
this.shaModifiedMonitor = new Object();
this.setScriptText(script);
this.resultType = resultType;
}
public void afterPropertiesSet() {
Assert.state(this.scriptSource != null, "Either script, script location, or script source is required");
}
public String getSha1() {
synchronized(this.shaModifiedMonitor) {
if (this.sha1 == null || this.scriptSource.isModified()) {
this.sha1 = DigestUtils.sha1DigestAsHex(this.getScriptAsString());
}
return this.sha1;
}
}
@Nullable
public Class<T> getResultType() {
return this.resultType;
}
public String getScriptAsString() {
try {
return this.scriptSource.getScriptAsString();
} catch (IOException var2) {
throw new ScriptingException("Error reading script text", var2);
}
}
public void setResultType(@Nullable Class<T> resultType) {
this.resultType = resultType;
}
public void setScriptText(String scriptText) {
this.scriptSource = new StaticScriptSource(scriptText);
}
public void setLocation(Resource scriptLocation) {
this.scriptSource = new ResourceScriptSource(scriptLocation);
}
public void setScriptSource(ScriptSource scriptSource) {
this.scriptSource = scriptSource;
}
}
?????????
RedisTemplate:stringRedisTemplate继承了redisTemplate,也可执行lua脚本
public class RedisTemplate<K, V> extends RedisAccessor implements RedisOperations<K, V>, BeanClassLoaderAware {
public <T> T execute(RedisScript<T> script, List<K> keys, Object... args) {
return this.scriptExecutor.execute(script, keys, args);
}
public <T> T execute(RedisScript<T> script, RedisSerializer<?> argsSerializer, RedisSerializer<T> resultSerializer, List<K> keys, Object... args) {
return this.scriptExecutor.execute(script, argsSerializer, resultSerializer, keys, args);
}
???????????
***********
示例
???????
test.lua
??????????????????????
redis.call("set",KEYS[1], ARGV[1])
return redis.call("get", KEYS[1])
?????????
RedisLuaTest
@Component
public class RedisLuaTest {
@Value("classpath:/lua/test.lua")
private Resource resource;
@javax.annotation.Resource
private StringRedisTemplate stringRedisTemplate;
@PostConstruct
public void init(){
DefaultRedisScript<String> redisScript = new DefaultRedisScript<>();
redisScript.setLocation(resource);
redisScript.setResultType(String.class);
System.out.println("redisScript.getSha1():"+redisScript.getSha1());
System.out.println("redisScript.getScriptAsString():"+redisScript.getScriptAsString());
String value = stringRedisTemplate.execute(redisScript, Collections.singletonList("key"), "瓜田李下");
System.out.println(value);
}
}
????????
控制台输出
2022-06-19 19:50:59.094 INFO 886 --- [ main] .s.d.r.c.RepositoryConfigurationDelegate : Finished Spring Data repository scanning in 4 ms. Found 0 Redis repository interfaces.
2022-06-19 19:50:59.378 INFO 886 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat initialized with port(s): 8080 (http)
2022-06-19 19:50:59.384 INFO 886 --- [ main] o.apache.catalina.core.StandardService : Starting service [Tomcat]
2022-06-19 19:50:59.385 INFO 886 --- [ main] org.apache.catalina.core.StandardEngine : Starting Servlet engine: [Apache Tomcat/9.0.63]
2022-06-19 19:50:59.468 INFO 886 --- [ main] o.a.c.c.C.[Tomcat].[localhost].[/] : Initializing Spring embedded WebApplicationContext
2022-06-19 19:50:59.468 INFO 886 --- [ main] w.s.c.ServletWebServerApplicationContext : Root WebApplicationContext: initialization completed in 850 ms
redisScript.getSha1():6c45d62f3875095a03105f73b04c64e77b17fa1e
redisScript.getScriptAsString():redis.call("set",KEYS[1], ARGV[1])
return redis.call("get", KEYS[1])
瓜田李下
2022-06-19 19:51:00.240 INFO 886 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat started on port(s): 8080 (http) with context path ''
2022-06-19 19:51:00.249 INFO 886 --- [ main] com.example.demo.DemoApplication : Started DemoApplication in 2.043 seconds (JVM running for 2.604)
?????????
?????????????????
|