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中运行如下图所示:

jediscluster 执行lua脚本 java使用lua脚本操作 redis_redis

2.3、直接在Redis客户端验证

然后再去自带的客户端验证
如下所示,keys *

jediscluster 执行lua脚本 java使用lua脚本操作 redis_lua_02

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运行截图

我们会发现每一次循环的报错都不回影响下一次循环,如下图所示:

jediscluster 执行lua脚本 java使用lua脚本操作 redis_redis_03

3.3、直接在Redis客户端验证

jediscluster 执行lua脚本 java使用lua脚本操作 redis_Redis_04

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。

jediscluster 执行lua脚本 java使用lua脚本操作 redis_Lua_05

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运行截图

jediscluster 执行lua脚本 java使用lua脚本操作 redis_Redis_06

4.2.3、直接在Redis客户端验证

在Redis自带的客户端我们可以看到product_count_10016还是自己的初始值,说明事务在报错时回滚了,如下图所示:

jediscluster 执行lua脚本 java使用lua脚本操作 redis_Lua_07

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