Jedis结合Lua脚本,如何编写长尾词?

2026-04-01 19:591阅读0评论SEO资讯
  • 内容介绍
  • 相关推荐

本文共计1814个文字,预计阅读时间需要8分钟。

Jedis结合Lua脚本,如何编写长尾词?

使用Lua脚本的优势+1、减少网络开销:可以将多个请求通过脚本的形式一次性发送,减少网络延迟和请求次数。+2、原子性操作:Redis会将整个脚本作为一个整体执行,中间不会被其他命令打断。

使用Lua脚本的好处 1、减少网络开销:可以将多个请求通过脚本的形式一次发送,减少网络时延和请求次数。

2、原子性的操作: Redis会将整个脚本作为一个整体执行,中间不会被其他命令插入。因此在编写脚本的过程中无需担心会出现竞态条件,无需使用事务。

3、代码复用:客户端发送的脚步会永久存在redis中,这样,其他客户端可以复用这一脚本来完成相同的逻辑。

4、速度快:见 与其它语言的性能比较, 还有一个 JIT编译器可以显著地提高多数任务的性能; 对于那些仍然对性能不满意的人, 可以把关键部分使用C实现, 然后与其集成, 这样还可以享受其它方面的好处。

5、可以移植:只要是有ANSI C 编译器的平台都可以编译,你可以看到它可以在几乎所有的平台上运行:从 Windows 到Linux,同样Mac平台也没问题, 再到移动平台、游戏主机,甚至浏览器也可以完美使用 (翻译成JavaScript).

6、源码小巧:20000行C代码,可以编译进182K的可执行文件,加载快,运行快。
eval 命令 (1)首先需要了解Redis eval 命令: eval 命令也会将脚本添加到脚本缓存中,但是它会立即对输入的脚本进行求值。
eg EVAL script numkeys key [key ...] arg [arg ...]  
script参数是一段Lua脚本程序,它会被运行在Redis服务器上下文中,这段脚本不必(也不应该)定义为一个Lua函数。 numkeys参数用于指定键名参数的个数。 键名参数 key [key ...] 从EVAL的第三个参数开始算起,表示在脚本中所用到的那些Redis键(key),这些键名参数可以在 Lua中通过全局变量KEYS数组,用1为基址的形式访问( KEYS[1] , KEYS[2] ,以此类推)。 在命令的最后,那些不是键名参数的附加参数 arg [arg ...] ,可以在Lua中通过全局变量ARGV数组访问,访问的形式和KEYS变量类似( ARGV[1] 、 ARGV[2] ,诸如此类) 如: eval "return {KEYS[1],KEYS[2],ARGV[1],ARGV[2]}" 2 username age jack 20

一般java运行lua脚本,采用的也是类似上述表达式,后面描述

(2)对于一段长的lua脚本,可以将脚本放在一个文件中,通过如下命令执行lua脚本

Jedis结合Lua脚本,如何编写长尾词?

$ redis-cli --eval path/to/redis.lua KEYS[1] KEYS[2] , ARGV[1] ARGV[2] ...

--eval,告诉redis-cli读取并运行后面的lua脚本
path/to/redis.lua,是lua脚本的位置
KEYS[1] KEYS[2],是要操作的键,可以指定多个,在lua脚本中通过KEYS[1], KEYS[2]获取
ARGV[1] ARGV[2],参数,在lua脚本中通过ARGV[1], ARGV[2]获取。

注意: KEYS和ARGV中间的 ‘,‘ 两边的空格,不能省略。

看下面例子:

在如下文件夹中以一个lua脚本 jedisCallLuaTest.lua

local key=KEYS[1] local args=ARGV
//说明:设置一个key = userName,value=Jack,20s过期时间 return redis.call("setex",key,unpack(args))

去客户端获取:

过期了。。。。


EVALSHA命令

将脚本script添加到脚本缓存中,但并不立即执行这个脚本。

语法如下:

redis 127.0.0.1:6379> EVALSHA sha1 numkeys key [key ...] arg [arg ...]

参数说明:

  • sha1: 通过 SCRIPT LOAD 生成的 sha1 校验码。
  • numkeys: 用于指定键名参数的个数。
  • key [key ...]: 从 EVAL 的第三个参数开始算起,表示在脚本中所用到的那些 Redis 键(key),这些键名参数可以在 Lua 中通过全局变量 KEYS 数组,用 1 为基址的形式访问( KEYS[1] , KEYS[2] ,以此类推)。
  • arg [arg ...]: 附加参数,在 Lua 中通过全局变量 ARGV 数组访问,访问的形式和 KEYS 变量类似( ARGV[1] 、 ARGV[2] ,诸如此类)。

eg:


整合Jedis + lua

(1)Jedis的使用
创建Jedis对象,主要是用于单个redis

publicclassRedisClient{ privatestaticJedisPooljedisPool=null; privatestaticStringaddr="127.0.0.1"; privatestaticintport=6379; static{ try{ JedisPoolConfigconfig=newJedisPoolConfig(); //连接耗尽时是否阻塞,false报异常,ture阻塞直到超时,默认true config.setBlockWhenExhausted(true); //设置的逐出策略类名,默认DefaultEvictionPolicy(当连接超过最大空闲时间,或连接数超过最大空闲连接数) config.setEvictionPolicyClassName("org.apache.commons.pool2.impl.DefaultEvictionPolicy"); //是否启用pool的jmx管理功能,默认true config.setJmxEnabled(true); //MBeanObjectName=new //ObjectName("org.apache.commons.pool2:type=GenericObjectPool,name=" //+"pool"+i);默认为"pool",JMX不熟,具体不知道是干啥的...默认就好. config.setJmxNamePrefix("pool"); //是否启用后进先出,默认true config.setLifo(true); //最大空闲连接数,默认8个 config.setMaxIdle(8); //最大连接数,默认8个 config.setMaxTotal(8); //获取连接时的最大等待毫秒数(如果设置为阻塞时BlockWhenExhausted),如果超时就抛异常,小于零:阻塞不确定的时间, //默认-1 config.setMaxWaitMillis(-1); //逐出连接的最小空闲时间默认1800000毫秒(30分钟) config.setMinEvictableIdleTimeMillis(1800000); //最小空闲连接数,默认0 config.setMinIdle(0); //每次逐出检查时逐出的最大数目如果为负数就是:1/abs(n),默认3 config.setNumTestsPerEvictionRun(3); //对象空闲多久后逐出,当空闲时间>该值且空闲连接>最大空闲数 //时直接逐出,不再根据MinEvictableIdleTimeMillis判断(默认逐出策略) config.setSoftMinEvictableIdleTimeMillis(1800000); //在获取连接的时候检查有效性,默认false config.setTestOnBorrow(false); //在空闲时检查有效性,默认false config.setTestWhileIdle(false); //逐出扫描的时间间隔(毫秒)如果为负数,则不运行逐出线程,默认-1 config.setTimeBetweenEvictionRunsMillis(-1); jedisPool=newJedisPool(config,addr,port,3000); }catch(Exceptione){ e.printStackTrace(); } } publicsynchronizedstaticJedisgetJedis(){ try{ if(jedisPool!=null){ Jedisresource=jedisPool.getResource(); returnresource; }else{ returnnull; } }catch(Exceptione){ e.printStackTrace(); returnnull; } } publicstaticvoidclose(finalJedisjedis){ if(jedis!=null){ jedis.close(); } } /*---------------------测试---------------------------*/ publicstaticvoidmain(java.lang.String[]args){ Jedisjedis=RedisClient.getJedis(); //dosomething RedisClient.testCallLua(jedis); RedisClient.close(jedis); } publicstaticvoidtestCallLua(Jedisjedis){ StringluaStr="return{KEYS[1],KEYS[1],ARGV[1],ARGV[2]}"; Objectresult=jedis.eval(luaStr,Lists.newArrayList("userName","age"),Lists.newArrayList("Jack","20")); System.out.println(result); } }

运行结果:

[userName,userName,Jack,20]

同理将工程中的lua文件加载成String,作为参数运行也是可行的。

java: 该段代码图个方便,引用了guava.jar包

/** *调用lua脚本 *@paramjedis */ publicstaticvoidtestCallLuaFile(Jedisjedis){ StringluaStr=null; //带反斜杠,路径为classPath,不带反斜杠,路径为类的同一目录 Readerr=newInputStreamReader(RedisClient.class.getResourceAsStream("/jedisCallLuaTest.lua")); try{ luaStr=CharStreams.toString(r); Objectresult=jedis.eval(luaStr,Lists.newArrayList("userName"),Lists.newArrayList("20","Tom")); System.out.println(result); }catch(IOExceptione){ e.printStackTrace(); } }

注意:getResourceAsStream()这可是好帮手,用到将文件内容加载成String,一定要想到他。CharStreams是guava.jar中的对象。

Reader转String还有如下两种伎俩:(发散。。。。)

@Test public void apcheIo() throws IOException{ String luaStr=null; //带反斜杠,路径为classPath,不带反斜杠,路径为类的同一目录 Reader r= new InputStreamReader(Reader2StrDemo.class.getResourceAsStream("/jedisCallLuaTest.lua")); luaStr = org.apache.commons.io.IOUtils.toString(r); System.out.println(luaStr); } @Test public void java8() throws IOException{ String luaStr=null; String path = "F:\\xxxx\\ideaProjects\\java8-pro\\resource\\jedisCallLuaTest.lua"; luaStr = Files.lines(Paths.get(path),Charset.defaultCharset()).collect(Collectors.joining()); System.out.println(luaStr); }

运行结果:

本文共计1814个文字,预计阅读时间需要8分钟。

Jedis结合Lua脚本,如何编写长尾词?

使用Lua脚本的优势+1、减少网络开销:可以将多个请求通过脚本的形式一次性发送,减少网络延迟和请求次数。+2、原子性操作:Redis会将整个脚本作为一个整体执行,中间不会被其他命令打断。

使用Lua脚本的好处 1、减少网络开销:可以将多个请求通过脚本的形式一次发送,减少网络时延和请求次数。

2、原子性的操作: Redis会将整个脚本作为一个整体执行,中间不会被其他命令插入。因此在编写脚本的过程中无需担心会出现竞态条件,无需使用事务。

3、代码复用:客户端发送的脚步会永久存在redis中,这样,其他客户端可以复用这一脚本来完成相同的逻辑。

4、速度快:见 与其它语言的性能比较, 还有一个 JIT编译器可以显著地提高多数任务的性能; 对于那些仍然对性能不满意的人, 可以把关键部分使用C实现, 然后与其集成, 这样还可以享受其它方面的好处。

5、可以移植:只要是有ANSI C 编译器的平台都可以编译,你可以看到它可以在几乎所有的平台上运行:从 Windows 到Linux,同样Mac平台也没问题, 再到移动平台、游戏主机,甚至浏览器也可以完美使用 (翻译成JavaScript).

6、源码小巧:20000行C代码,可以编译进182K的可执行文件,加载快,运行快。
eval 命令 (1)首先需要了解Redis eval 命令: eval 命令也会将脚本添加到脚本缓存中,但是它会立即对输入的脚本进行求值。
eg EVAL script numkeys key [key ...] arg [arg ...]  
script参数是一段Lua脚本程序,它会被运行在Redis服务器上下文中,这段脚本不必(也不应该)定义为一个Lua函数。 numkeys参数用于指定键名参数的个数。 键名参数 key [key ...] 从EVAL的第三个参数开始算起,表示在脚本中所用到的那些Redis键(key),这些键名参数可以在 Lua中通过全局变量KEYS数组,用1为基址的形式访问( KEYS[1] , KEYS[2] ,以此类推)。 在命令的最后,那些不是键名参数的附加参数 arg [arg ...] ,可以在Lua中通过全局变量ARGV数组访问,访问的形式和KEYS变量类似( ARGV[1] 、 ARGV[2] ,诸如此类) 如: eval "return {KEYS[1],KEYS[2],ARGV[1],ARGV[2]}" 2 username age jack 20

一般java运行lua脚本,采用的也是类似上述表达式,后面描述

(2)对于一段长的lua脚本,可以将脚本放在一个文件中,通过如下命令执行lua脚本

Jedis结合Lua脚本,如何编写长尾词?

$ redis-cli --eval path/to/redis.lua KEYS[1] KEYS[2] , ARGV[1] ARGV[2] ...

--eval,告诉redis-cli读取并运行后面的lua脚本
path/to/redis.lua,是lua脚本的位置
KEYS[1] KEYS[2],是要操作的键,可以指定多个,在lua脚本中通过KEYS[1], KEYS[2]获取
ARGV[1] ARGV[2],参数,在lua脚本中通过ARGV[1], ARGV[2]获取。

注意: KEYS和ARGV中间的 ‘,‘ 两边的空格,不能省略。

看下面例子:

在如下文件夹中以一个lua脚本 jedisCallLuaTest.lua

local key=KEYS[1] local args=ARGV
//说明:设置一个key = userName,value=Jack,20s过期时间 return redis.call("setex",key,unpack(args))

去客户端获取:

过期了。。。。


EVALSHA命令

将脚本script添加到脚本缓存中,但并不立即执行这个脚本。

语法如下:

redis 127.0.0.1:6379> EVALSHA sha1 numkeys key [key ...] arg [arg ...]

参数说明:

  • sha1: 通过 SCRIPT LOAD 生成的 sha1 校验码。
  • numkeys: 用于指定键名参数的个数。
  • key [key ...]: 从 EVAL 的第三个参数开始算起,表示在脚本中所用到的那些 Redis 键(key),这些键名参数可以在 Lua 中通过全局变量 KEYS 数组,用 1 为基址的形式访问( KEYS[1] , KEYS[2] ,以此类推)。
  • arg [arg ...]: 附加参数,在 Lua 中通过全局变量 ARGV 数组访问,访问的形式和 KEYS 变量类似( ARGV[1] 、 ARGV[2] ,诸如此类)。

eg:


整合Jedis + lua

(1)Jedis的使用
创建Jedis对象,主要是用于单个redis

publicclassRedisClient{ privatestaticJedisPooljedisPool=null; privatestaticStringaddr="127.0.0.1"; privatestaticintport=6379; static{ try{ JedisPoolConfigconfig=newJedisPoolConfig(); //连接耗尽时是否阻塞,false报异常,ture阻塞直到超时,默认true config.setBlockWhenExhausted(true); //设置的逐出策略类名,默认DefaultEvictionPolicy(当连接超过最大空闲时间,或连接数超过最大空闲连接数) config.setEvictionPolicyClassName("org.apache.commons.pool2.impl.DefaultEvictionPolicy"); //是否启用pool的jmx管理功能,默认true config.setJmxEnabled(true); //MBeanObjectName=new //ObjectName("org.apache.commons.pool2:type=GenericObjectPool,name=" //+"pool"+i);默认为"pool",JMX不熟,具体不知道是干啥的...默认就好. config.setJmxNamePrefix("pool"); //是否启用后进先出,默认true config.setLifo(true); //最大空闲连接数,默认8个 config.setMaxIdle(8); //最大连接数,默认8个 config.setMaxTotal(8); //获取连接时的最大等待毫秒数(如果设置为阻塞时BlockWhenExhausted),如果超时就抛异常,小于零:阻塞不确定的时间, //默认-1 config.setMaxWaitMillis(-1); //逐出连接的最小空闲时间默认1800000毫秒(30分钟) config.setMinEvictableIdleTimeMillis(1800000); //最小空闲连接数,默认0 config.setMinIdle(0); //每次逐出检查时逐出的最大数目如果为负数就是:1/abs(n),默认3 config.setNumTestsPerEvictionRun(3); //对象空闲多久后逐出,当空闲时间>该值且空闲连接>最大空闲数 //时直接逐出,不再根据MinEvictableIdleTimeMillis判断(默认逐出策略) config.setSoftMinEvictableIdleTimeMillis(1800000); //在获取连接的时候检查有效性,默认false config.setTestOnBorrow(false); //在空闲时检查有效性,默认false config.setTestWhileIdle(false); //逐出扫描的时间间隔(毫秒)如果为负数,则不运行逐出线程,默认-1 config.setTimeBetweenEvictionRunsMillis(-1); jedisPool=newJedisPool(config,addr,port,3000); }catch(Exceptione){ e.printStackTrace(); } } publicsynchronizedstaticJedisgetJedis(){ try{ if(jedisPool!=null){ Jedisresource=jedisPool.getResource(); returnresource; }else{ returnnull; } }catch(Exceptione){ e.printStackTrace(); returnnull; } } publicstaticvoidclose(finalJedisjedis){ if(jedis!=null){ jedis.close(); } } /*---------------------测试---------------------------*/ publicstaticvoidmain(java.lang.String[]args){ Jedisjedis=RedisClient.getJedis(); //dosomething RedisClient.testCallLua(jedis); RedisClient.close(jedis); } publicstaticvoidtestCallLua(Jedisjedis){ StringluaStr="return{KEYS[1],KEYS[1],ARGV[1],ARGV[2]}"; Objectresult=jedis.eval(luaStr,Lists.newArrayList("userName","age"),Lists.newArrayList("Jack","20")); System.out.println(result); } }

运行结果:

[userName,userName,Jack,20]

同理将工程中的lua文件加载成String,作为参数运行也是可行的。

java: 该段代码图个方便,引用了guava.jar包

/** *调用lua脚本 *@paramjedis */ publicstaticvoidtestCallLuaFile(Jedisjedis){ StringluaStr=null; //带反斜杠,路径为classPath,不带反斜杠,路径为类的同一目录 Readerr=newInputStreamReader(RedisClient.class.getResourceAsStream("/jedisCallLuaTest.lua")); try{ luaStr=CharStreams.toString(r); Objectresult=jedis.eval(luaStr,Lists.newArrayList("userName"),Lists.newArrayList("20","Tom")); System.out.println(result); }catch(IOExceptione){ e.printStackTrace(); } }

注意:getResourceAsStream()这可是好帮手,用到将文件内容加载成String,一定要想到他。CharStreams是guava.jar中的对象。

Reader转String还有如下两种伎俩:(发散。。。。)

@Test public void apcheIo() throws IOException{ String luaStr=null; //带反斜杠,路径为classPath,不带反斜杠,路径为类的同一目录 Reader r= new InputStreamReader(Reader2StrDemo.class.getResourceAsStream("/jedisCallLuaTest.lua")); luaStr = org.apache.commons.io.IOUtils.toString(r); System.out.println(luaStr); } @Test public void java8() throws IOException{ String luaStr=null; String path = "F:\\xxxx\\ideaProjects\\java8-pro\\resource\\jedisCallLuaTest.lua"; luaStr = Files.lines(Paths.get(path),Charset.defaultCharset()).collect(Collectors.joining()); System.out.println(luaStr); }

运行结果: