0%

Redis + Lua

搭建一个 springboot 的项目,引入 spring-boot-starter-parent父组件, spring-boot-starter-web组件, lombok组件,jedis客户端组件 ,谷歌的guava 组件

创建 Runners

@Slf4j
@Component
public class Runners implements ApplicationRunner {
@Override
public void run(ApplicationArguments args) throws Exception {
Jedis jedis = new Jedis("127.0.0.1");
String lua = "return ARGV[1]"; // lua 脚本
String result = (String)jedis.eval(lua,0,"100"); // 0个key 值100
log.info("result:{}",result);
}
}

启动项目 result 打印 100
命令格式

EVAL lua脚本 numkeys key [key ...] arg [arg ...]
  • numkeys 指 key 的数量
  • key [key ...],键,多个,在lua脚本中通过KEYS[1], KEYS[2]获取
  • arg [arg ...],值,多个,在lua脚本中通过ARGV[1], ARGV[2]获取

ScriptingCommandsjedislua 命令的支持

public interface ScriptingCommands {
Object eval(String script, int keyCount, String... params);

Object eval(String script, List<String> keys, List<String> args);

Object eval(String script);

Object evalsha(String sha1);

Object evalsha(String sha1, List<String> keys, List<String> args);

Object evalsha(String sha1, int keyCount, String... params);

Boolean scriptExists(String sha1);

List<Boolean> scriptExists(String... sha1);

String scriptLoad(String script);
}

Runners 类改造

Jedis jedis = new Jedis("127.0.0.1");
String lua = "redis.call('set', KEYS[1], ARGV[1])";
List<String> keys = new ArrayList<>();
List<String> values = new ArrayList<>();
keys.add("key");
values.add("100");
jedis.eval(lua,keys,values);

打开 redis 客户端

127.0.0.1:6379> get key
"100"

或者 修改

String lua = "local str = redis.call('get', KEYS[1]);return str";
String result = (String) jedis.eval(lua,keys,values);
log.info(result);

控制台打印 100
使用 Spring Data Redis 组件,支持的客户端 Jedis / Lettuce,配置 RedisTemplate

@Configuration
@AutoConfigureAfter(RedisAutoConfiguration.class)
public class RedisConfig {
/**
* 默认情况下的模板只能支持RedisTemplate<String, String>,也就是只能存入字符串,因此支持序列化
*/
@Bean
public RedisTemplate<String, Serializable> redisTemplate(LettuceConnectionFactory redisConnectionFactory) {
RedisTemplate<String, Serializable> template = new RedisTemplate<>();
template.setKeySerializer(new StringRedisSerializer());
template.setValueSerializer(new GenericJackson2JsonRedisSerializer());
template.setConnectionFactory(redisConnectionFactory);
return template;
}
}
@Slf4j
@Component
public class Runners implements ApplicationRunner {

@Autowired
private RedisTemplate redisTemplate;

@Override
public void run(ApplicationArguments args) throws Exception {
String lua = "redis.call('EXPIRE', KEYS[1], ARGV[2]);"; // 这里使用的是第二个参数30, 101未用到
ImmutableList<Object> keys = ImmutableList.of("key");
RedisScript<Number> redisScript = new DefaultRedisScript<>(lua, Number.class);
redisTemplate.execute(redisScript,keys,"101",30);
}
}

key 有效时间为 30 秒,30 秒之后查询 nil

127.0.0.1:6379> get key
"100"
127.0.0.1:6379> get key
(nil)

redis 可视化客户端 https://github.com/qishibo/AnotherRedisDesktopManager,查看有效时间30秒递减
更改 Runners

String lua = " redis.call('INCR',KEYS[1]);";
ImmutableList<Object> keys = ImmutableList.of("key");
RedisScript<Number> redisScript = new DefaultRedisScript<>(lua, Number.class);
redisTemplate.execute(redisScript,keys,"100",120);

执行命令,值增加 1

127.0.0.1:6379> get key
"100"
127.0.0.1:6379> get key
"101"
127.0.0.1:6379>

以上例子涉及 redis 四个命令 SETGETEXPIREINCR

INCR 对存储在指定 key的数值执行原子的加1操作
如果指定的key不存在,那么在执行incr操作之前,会先将它的值设定为0

EXPIRE 设置key的过期时间,超过时间后,将会自动删除该key

查看其它命令 http://www.redis.cn/commands.html
详细lua学习 https://github.com/52fhy/lua-book