1.问题
原子性问题
Redis虽然是单线程的,但是仍然可能会出现线程安全问题,当然这个线程安全问题不是来源于Redis服务器内部,而是多个客户端去操作Redis的时候,多个客户端的操作就相当于同一个进程下的多个线程,如果多个客户端之间没有做好同步策略,就会产生数据不一致的问题。比如:
我想按照顺序执行几个Redis命令,但是多个客户端的命令之间没有做请求同步,导致实际执行顺序与预想的不一致,最终的结果也就无法满足原子性。
执行效率问题
Redis由于是基于内存的数据库,它本身的吞吐量是非常高的,那么影响Redis吞吐量的一个重要因素是网络,我们用redis实现某些特定功能,很可能需要用多个命令、多个数据类型、多个步骤一起才能完成功能,那么在实现这个功能的过程中,就会产生多次对Redis的网络访问,多次向Redis发送和获取数据,这种多次网络请求对性能影响是非常大的。当然我们可以使用连接池、pipeline管道操作,但是它们有一定的局限性和应用场景,如果我们能将一系列的对redis的操作变成一次操作,那会大大地减少网络请求,提升效率;
Lua语言
Lua是一个高效的轻量级脚本语言 (javascript、shell、sql、perl、python),用标准C语言编写并以源代码形式开放,其设计目的是为了嵌入应用程序中,从而为应用程序提供灵活的扩展和定制功能;
Redis中内嵌了Lua运行环境,我们可以使用Lua语言编写脚本传到Redis中执行,执行完之后,redis会把结果返回给我们客户端,Redis客户端可以使用Lua 脚本,直接在服务端原子性地执行多个Redis命令。
使用脚本的好处:
1、原子操作,redis会将整个脚本作为一个整体执行,中间不会被其他命令插入,换句话说,编写脚本的过程中无需担心会出现插队、竞争等条件;
2、减少网络开销,在Lua脚本中可以把多个命令放在同一个脚本中运行;
2.在Redis中使用Lua脚本
我们可以编写Lua脚本,然后再Lua脚本中调用Redis命令,使用redis.call函数调用。
比如调用string类型的命令:
eval "redis.call('set', 'k1', 'v1')" 0
eval "return redis.call('get','k1')" 0
eval "redis.call('set', KEYS[1], ARGV[1])" 1 K2 V2
eval "return redis.call('get', KEYS[1])" 1 K2
KEYS[1]表示key
ARGV[1]表示value
我们还可以写一个lua脚本文件,并把脚本文件保存起来,然后执行lua脚本文件:
abc.lua脚本文件代码:
redis.call('set', 'k1', 'v1')
return redis.call('get', 'k1')
执行lua脚本:
./redis-cli --eval /root/abc.lua -a 123456
其中–eval是执行,/root/abc.lua是脚本文件,-a 123456是指定redis密码
在脚本中可以使用return 语句将值返回给redis客户端,如果没有执行return, 默认返回为nil。
redis会自动将脚本返回值的Lua数据类型转化为Redis的返回值类型,然后返回给我们客户端。
EVAL命令的格式
[EVAL] [脚本内容] [key参数的数量] [key …] [arg …]
可以通过key和arg这两个参数向脚本中传递参数数据,这两个参数在脚本中分别使用KEYS和ARGV 这两个类型的全局变量接收,KEYS和ARGV 必须大写。
当脚本不需要任何参数时,需要传递一个0参数;
3.jedis使用
Jedis jedis = JedisPoolInstance.getJedisPoolInstance().getResource();
// 设置值
Object eval = jedis.eval("return redis.call('set','k2','v2') ",0);
System.out.println(eval);
// 取出值
Object k2 = jedis.eval("return redis.call('get',KEYS[1])", 1, "k2");
System.out.println(k2);
4.在Spring中调用
DefaultRedisScript<String> redisScript = new DefaultRedisScript<String>();
redisScript.setResultType(String.class);
redisScript.setScriptText("return redis.call('get', 'k1')");
//加载classpath下的一个lua脚本文件
//redisScript.setScriptSource(new ResourceScriptSource(new ClassPathResource("limit.lua")));
Object object = redisTemplate.execute(redisScript, null);
System.out.println(object);
来源:CSDN
作者:qq_320253624
链接:https://blog.csdn.net/qq_36331657/article/details/104107892