标签:gre 内存 自定义 内存限制 ref redis 客户 argv 其他
Redis Lua 这个技术,我之前就在关注,今天有空,我把项目中基于Redis实现的ID生成器改成用lua脚本实现,防止并发id冲突问题
从定义上来说, Redis 中的脚本本身就是一种事务, 所以任何在事务里可以完成的事, 在脚本里面也能完成。 并且一般来说, 使用脚本要来得更简单,并且速度更快。
使用事务时可能会遇上以下两种错误:
maxmemory
设置了最大内存限制的话)。对于发生在 EXEC 执行之前的错误,客户端以前的做法是检查命令入队所得的返回值:如果命令入队时返回 QUEUED
,那么入队成功;否则,就是入队失败。如果有命令在入队时失败,那么大部分客户端都会停止并取消这个事务。
不过,从 Redis 2.6.5 开始,服务器会对命令入队失败的情况进行记录,并在客户端调用 EXEC 命令时,拒绝执行并自动放弃这个事务。
在 Redis 2.6.5 以前, Redis 只执行事务中那些入队成功的命令,而忽略那些入队失败的命令。 而新的处理方式则使得在流水线(pipeline)中包含事务变得简单,因为发送事务和读取事务的回复都只需要和服务器进行一次通讯。
至于那些在 EXEC 命令执行之后所产生的错误, 并没有对它们进行特别处理: 即使事务中有某个/某些命令在执行时产生了错误, 事务中的其他命令仍然会继续执行。
经过测试lua中发生异常处理方式和redis 事务一致,可以说这两个东西是一样的,但是lua支持缓存,可以复用脚本,这个是原来的事务所没有的
了解更多事务相关信息,看这个网站
在redis里面使用lua脚本主要用三个命令
EVAL script numkeys key [key ...] arg [arg ...] > eval "return {KEYS[1],KEYS[2],ARGV[1],ARGV[2]}" 2 key1 key2 first second 1) "key1" 2) "key2" 3) "first" 4) "second"
key代表要操作的rediskey
arg可以传自定义的参数
numkeys用来确定key有几个
script就是你写的lua脚本
lua脚本里面使用KEYS[1]和ARGV[1]来获取传递的key和arg
lua语法详见lua教程
SCRIPT LOAD script EVALSHA sha1 numkeys key [key ...] arg [arg ...] redis> SCRIPT LOAD "return ‘hello moto‘" "232fd51614574cf0867b83d384a5e898cfd24e5a" redis> EVALSHA 232fd51614574cf0867b83d384a5e898cfd24e5a 0 "hello moto"
SHA1有如下特性:不可以从消息摘要中复原信息;两个不同的消息不会产生同样的消息摘要,(但会有1x10 ^ 48分之一的机率出现相同的消息摘要,一般使用时忽略)。
上面讲的是如何在redis控制台调用lua脚本,现在我们来讲下怎么在java里面调用
在java里面调用redis一般使用jedis,对于调用lua脚本来讲,spring-data-redis包做的封装使用起来更加方便,底层也是基于jiedis,所以我们这边直接讲spring-data-redis中的redisTemplate如何来调用lua
先导入依赖
<dependency> <groupId>org.springframework.data</groupId> <artifactId>spring-data-redis</artifactId> <version>1.8.1.RELEASE</version> </dependency>
然后我们使用StringRedisTemplate这个类来操作
@Resource private StringRedisTemplate stringRedisTemplate; public <T> T runLua(String fileClasspath, Class<T> returnType, List<String> keys, Object ... values){ DefaultRedisScript<T> redisScript =new DefaultRedisScript<>(); redisScript.setScriptSource(new ResourceScriptSource(new ClassPathResource(fileClasspath))); redisScript.setResultType(returnType); return stringRedisTemplate.execute(redisScript,keys,values); }
protected <T> T eval(RedisConnection connection, RedisScript<T> script, ReturnType returnType, int numKeys, byte[][] keysAndArgs, RedisSerializer<T> resultSerializer) { Object result; try { result = connection.evalSha(script.getSha1(), returnType, numKeys, keysAndArgs); } catch (Exception e) { if (!exceptionContainsNoScriptError(e)) { throw e instanceof RuntimeException ? (RuntimeException) e : new RedisSystemException(e.getMessage(), e); } result = connection.eval(scriptBytes(script), returnType, numKeys, keysAndArgs); } if (script.getResultType() == null) { return null; } return deserializeResult(resultSerializer, result); }
因为sha1的算法是通用的,所以在java客户端可以提前算出SHA1校验和,然后用evalsha来执行脚本,如果SHA1对应的脚本,那么还是用eval来执行,eval执行一次后,下次都可以直接调用evalsha了,减少网络开销
我们写完一个lua脚本,lua和redis的数据类型是不一致的,存在一个转换,并且如果遇到复杂逻辑的lua脚本,如果不能debug,只在自己脑子里面走这个逻辑,是不科学的,如果redis lua也提供了debug功能,要在redis客户端执行
在运行lua的eval,加上-ldb即可开启debug功能,debug只支持eval命令
./redis-cli --ldb --eval /tmp/script.lua mykey somekey , arg1 arg2
全文见:https://www.jianshu.com/p/366d1b4f0d13
标签:gre 内存 自定义 内存限制 ref redis 客户 argv 其他
原文地址:https://www.cnblogs.com/kitor/p/12757865.html