Jedis简单操作、Redis管道、Lua脚本以及Jedis简单示例
- 1、整体代码示例
- 2、Jedis简单操作
- 2.1、核心代码
- 2.2、Idea运行截图
- 2.3、直接在Redis客户端验证
- 3、Redis管道(Pipeline)
- 3.1、核心代码
- 3.2、Idea运行截图
- 3.3、直接在Redis客户端验证
- 4、Redis Lua脚本
- 4.1、在Redis客户端执行Lua脚本(后续还得再研究一下,这块不太理解)
- 4.2、Java调用Redis Lua脚本
- 4.2.1、核心代码
- 4.2.2、Idea运行截图
- 4.2.3、直接在Redis客户端验证
- 4.3、Redis Lua脚本的缺点
- 5、上述代码报错,不能操作Redis问题
1、整体代码示例
public class JedisSingleTest {
public static void main(String[] args) throws IOException {
JedisPoolConfig jedisPoolConfig = new JedisPoolConfig();
jedisPoolConfig.setMaxTotal(20);
jedisPoolConfig.setMaxIdle(10);
jedisPoolConfig.setMinIdle(5);
// timeout,这里既是连接超时又是读写超时,从Jedis 2.8开始有区分connectionTimeout和soTimeout的构造函数
JedisPool jedisPool = new JedisPool(jedisPoolConfig, "192.168.231.131", 6379, 3000, null);
Jedis jedis = null;
try {
//从redis连接池里拿出一个连接执行命令
jedis = jedisPool.getResource();
// Jedis 直接操作
/*System.out.println(jedis.set("ale", "cool"));
System.out.println(jedis.get("ale"));*/
// 管道示例
/*Pipeline pl = jedis.pipelined();
for (int i = 0; i < 10; i++) {
pl.incr("aleKey");
pl.set("ale" + i, "cool");
// 模拟管道报错
pl.setbit("ale",-1,true);
}
List<Object> results = pl.syncAndReturnAll();
System.out.println(results);*/
//lua脚本模拟一个商品减库存的原子操作
//lua脚本命令执行方式:redis-cli --eval /tmp/test.lua , 10
jedis.set("product_count_10016", "15"); //初始化商品10016的库存
String script = " local count = redis.call('get', KEYS[1]) " +
" local a = tonumber(count) " +
" local b = tonumber(ARGV[1]) " +
" b == 0 " + //这行是报错行
" if a >= b then " +
" redis.call('set', KEYS[1], a-b) " +
" return 1 " +
" end " +
" return 0 ";
Object obj = jedis.eval(script, Arrays.asList("product_count_10016"), Arrays.asList("10"));
System.out.println(obj);
} catch (Exception e) {
e.printStackTrace();
} finally {
//注意这里不是关闭连接,在JedisPool模式下,Jedis会被归还给资源池。
if (jedis != null) {
jedis.close();
}
}
}
}
2、Jedis简单操作
2.1、核心代码
System.out.println(jedis.set("ale", "cool"));
System.out.println(jedis.get("ale"));
2.2、Idea运行截图
这个代码的含义就是设置了一个字符串的数据,然后再拿出来,我们再Idea中运行如下图所示:
2.3、直接在Redis客户端验证
然后再去自带的客户端验证
如下所示,keys *
3、Redis管道(Pipeline)
为了节省网络建立连接以及传输数据的开销,我们可以使用管道的方式将一组操作放到管道中并发送给redis服务端去操作,这样就能节省时间的开销。
但也有缺点,就是管道没有原子性操作,某一条命令报错之后,会继续往下执行。
3.1、核心代码
Pipeline pl = jedis.pipelined();
for (int i = 0; i < 10; i++) {
pl.incr("aleKey");
pl.set("ale" + i, "cool");
// 模拟管道报错
pl.setbit("ale",-1,true);
}
List<Object> results = pl.syncAndReturnAll();
System.out.println(results);
3.2、Idea运行截图
我们会发现每一次循环的报错都不回影响下一次循环,如下图所示:
3.3、直接在Redis客户端验证
4、Redis Lua脚本
Redis在2.6推出了脚本功能,允许开发者使用Lua语言编写脚本传到Redis中执行。使用脚本的好处如下:
1、减少网络开销:可以传入批量命令使其进行操作,这点跟管道类似。
2、Redis会将整个脚本 作为一个整体执行,中间不会被其他命令插入。管道不是原子的,不过 redis的批量操作命令(类似mset)是原子的。
3、替代redis的事务功能:redis自带的事务功能很鸡肋,报错不支持回滚,而redis的lua脚本几乎实现了 常规的事务功能,支持报错回滚操作,官方推荐如果要使用redis的事务功能可以用redis lua替代。
4.1、在Redis客户端执行Lua脚本(后续还得再研究一下,这块不太理解)
格式如下:
EVAL script numkeys key [key …] arg [arg …]
eval “return {KEYS[1],KEYS[2],ARGV[1],ARGV[2]}” 2 key1 key2 first seco nd
就比如这一行在客户端执行的代码,前面的 KEYS[1],KEYS[2] 这些都是一些参数,后面的 key1 key2 这些都是一些值,中间的 2 代表有两个key。
4.2、Java调用Redis Lua脚本
4.2.1、核心代码
jedis.set("product_count_10016", "15"); //初始化商品10016的库存
String script = " local count = redis.call('get', KEYS[1]) " +
" local a = tonumber(count) " +
" local b = tonumber(ARGV[1]) " +
" if a >= b then " +
" redis.call('set', KEYS[1], a-b) " +
" return 1 " +
" end " +
" return 0 ";
Object obj = jedis.eval(script, Arrays.asList("product_count_10016"), Arrays.asList("10"));
System.out.println(obj);
4.2.2、Idea运行截图
4.2.3、直接在Redis客户端验证
在Redis自带的客户端我们可以看到product_count_10016还是自己的初始值,说明事务在报错时回滚了,如下图所示:
4.3、Redis Lua脚本的缺点
不要在Lua脚本中出现死循环和耗时的运算,否则redis会阻塞,将不接受其他的命令, 所以使用时要注意不能出现死循环、耗时的运算。redis是单进程、单线程执行脚本。管道会阻塞redis。
5、上述代码报错,不能操作Redis问题
要检查以下几个点:
1、检查Redis是否启动
2、检查linux防火墙是否关闭
关闭防火墙:systemctl stop firewalld
3、检查redis的配置文件 redis.conf 文件中 protected-mode 配置是否关闭
应改为:protected-mode no
4、检查流是否使用完未关闭等问题
5、redis.conf文件中注释掉bind 127.0.0.1