Jedis 操作 Redis 数据结构全攻略

news2024/11/15 0:42:38

Jedis 操作 Redis 数据结构全攻略

  • 一 . 认识 RESP
  • 二 . 前置操作
    • 2.1 创建项目
    • 2.2 关于开放 Redis 端口的问题
      • 2.2.1 端口转发?
      • 2.2.2 端口配置
    • 2.3 连接到 Redis 服务器
  • 三 . 通用命令
    • 3.1 set 和 get
    • 3.2 exists 和 del
    • 3.3 keys
    • 3.4 expire、ttl、type
  • 三 . string 相关命令
    • 3.1 mset / mget
    • 3.2 getrange / setrange
    • 3.3 append
    • 3.4 incr / decr
    • 3.5 mset、mget
    • 3.6 append、incr、decr
  • 四 . list 类型
    • 4.1 lpush、lrange、rpush
    • 4.2 lpop、rpop
    • 4.3 blpop 和 brpop
    • 4.4 llen
  • 五 . set 类型
    • 5.1 sadd、smembers
    • 5.2 sismember
    • 5.3 scard
    • 5.4 spop
    • 5.5 sinter、sinterstore
  • 六 . hash 类型
    • 6.1 hset、hget
    • 6.2 hexists
    • 6.3 hdel
    • 6.4 hkeys、hvals
    • 6.5 hmset、hmget
  • 七 . zset 类型
    • 7.1 zadd、zrange
    • 7.2 zcard
    • 7.3 zrem
    • 7.4 zscore
    • 7.5 zrank

Hello , 大家好 , 这个专栏给大家带来的是 Redis 系列 !
我们之前主要学习的是各种 Redis 的基本操作所对应的命令 , 都是在 Redis 的客户端的命令行中手动执行的 , 但是这种方式并不是日常开发主要的形式 .
在日常工作中 , 最常见的就是用程序来操作 Redis , 利用 Redis 提供的 API 来实现一些功能 .

在这里插入图片描述

本专栏旨在为初学者提供一个全面的 Redis 学习路径,从基础概念到实际应用,帮助读者快速掌握 Redis 的使用和管理技巧。通过本专栏的学习,能够构建坚实的 Redis 知识基础,并能够在实际学习以及工作中灵活运用 Redis 解决问题 .
专栏地址 : Redis 入门实践

一 . 认识 RESP

那为什么我们通过代码就可以编写出一个自定义的 Redis 客户端呢 ?
我们先来回顾一个知识 , 在网络通信过程中 , 会用到很多的协议 , 从下往上的顺序依次是 :

  1. 物理层
  2. 数据链路层 : 以太网协议
  3. 网络层 : IP 协议
  4. 传输层 : TCP/UDP 协议
  5. 应用层 : 自定义协议 (比如 : HTTP 协议)

虽然业界有许多成熟的自定义协议 , 但是更多的时候 , 大家都会选择自定义应用层协议 , Redis 应用层的协议就是自定义的协议 .
那客户端按照应用层协议发送请求 , 服务器按照协议内容进行解析 , 再按照这个协议去构造响应 , 客户端去解析响应 . 那我们要想通信能够完成 , 就需要开发客户端和开发服务器的程序员都非常清楚协议的细节 .
那如果我们想要开发一个 Redis 客户端 , 也需要知道 Redis 的应用层协议 , 我们也可以从官网中获取到 Redis 的应用层协议 : https://redis.io/docs/reference/protocol-spec
image.png
那 RESP 就是 Redis 的自定义的应用层协议的名字
我们来阅读以下这篇文档
image.png
简单好实现 : Redis 的客户端使用一种叫做 RESP 的协议来去跟 Redis 服务器进行通讯 .
image.png
可以快速进行解析 : RESP 可以识别不同的类型 , 比如整型、字符串、数组 .
image.png
客户端和服务器是基于 TCP 协议进行连接的 , 但是其实跟 TCP 关系没多大 , 只是借助 TCP 这个平台
image.png
Redis 客户端请求和响应是一问一答类型的

客户端给服务器发送一个请求 , 服务器返回给客户端一个响应

image.png
客户端给服务器发送的是 Redis 命令 , 以 bulk strings 数组的形式发送的
那不同的命令返回结果也不同 , 有的命令直接返回 ok , 有的命令可能返回整数 …
image.png
image.png
如果是简单的字符串 , 用 ‘+’ 表示 , 如果是简单的错误 , 用 ‘-’ 表示 …
下面还介绍了一个样例 , 服务端返回 OK 时候的表示
image.png
那服务器返回 OK 就会返回这串字符串 , 将这串字符串写入到 TCP 的 socket 中

因此 , Redis 的客户端和服务器要做的工作 , 就是

  1. 按照上述格式构造出字符串 , 写入 socket 中
  2. 从 socket 中读取字符串 , 按照上述格式进行解析

那我们介绍 RESP 只是希望大家了解一下 Redis 底层是怎样通信的 , 并不影响后续代码的编写

二 . 前置操作

Redis 在官网上公开了自定义的应用层协议 : RESP , 那任何一个第三方都可以根据约定好的协议就可以实现出一个和 Redis 服务器通信的客户端程序 .
那也有很多大佬已经实现好了现成的客户端程序 , 我们可以直接调用 , 就不必再关心里面的细节了 .
在 Java 中 , 比较常用的是 Jedis , 因为他所提供的 API 和 Redis 的命令非常相似 , 十分好上手 .
那我们创建一个 Maven 项目 , 来去演示一系列的操作

2.1 创建项目

打开我们的 IDEA
image.png
image.png
image.png
然后在 pom.xml 中引入 Jedis 的依赖

<!-- https://mvnrepository.com/artifact/redis.clients/jedis -->
<dependency>
    <groupId>redis.clients</groupId>
    <artifactId>jedis</artifactId>
    <version>4.4.3</version>
</dependency>

image.png
然后我们就可以新建一个类 , 来去模拟 Jedis 的操作了
image.png
image.png

2.2 关于开放 Redis 端口的问题

我们开发代码 , 一般都使用 Windows 系统 , 但是我们的 Redis 是存在云服务器上的 , 要是使用 127.0.0.1 访问到云服务器上面的 Redis 肯定是实现不了的
❓ 那我们修改成云服务器的外网 IP 再去访问行不 ?

也是不可以的 , 我们 Redis 的默认端口 6379 默认是被云服务器的防火墙给保护起来的 .

❓ 那我们再把 6379 给放开不就好了 ?

不可以 ! 千万不要这样做 ! Redis 是非常容易被黑客攻击的 , Redis 的端口一旦公开到网上 , 就特别容易被入侵 !
虽然我们之前开放过 tomcat 的端口 , 这是因为 tomcat 端口的安全性非常高 . 但是 Redis 的 6379 端口 , 安全性非常差 , 一旦你开了 , 不出 3 天 , 服务器就要死翘翘 .
那我给 Redis 换个端口 , 是不是就安全了 ? -> 掩耳盗铃 , 还是不安全的 .

那我们有两种方案解决 :

  1. 直接让 Java 程序也在 Linux 上面运行 [不推荐]

需要我们把代码编写完毕之后 , 打成一个 jar 包 , 然后把 jar 包拷贝到云服务器上去执行
比较麻烦

  1. 配置 SSH 端口转发 , 把云服务器的 Redis 端口 , 映射到本地主机 [推荐]

2.2.1 端口转发?

我们的 Windows 主机和云服务器是通过 SSH 连接起来的
无标题.png
那一个 Linux 主机上 , 需要映射的 Windows 设备有很多 , SSH 也有可能需要来给多个端口传递数据 , 这个时候 , 为了区分不同的端口 , 往往会把服务器的端口在本地用另外一个端口来表示
image.png
这样就实现了本地和云服务器端口的对应
只要咱们进行简单的配置 , 后续就把服务器的端口当成一个本地端口来使用即可

2.2.2 端口配置

打开我们的 XShell
image.png
image.png

SSH 端口转发 = SSH = SSH 隧道

image.png
image.png

只有当 SSH 连接上了之后 , 端口转发才生效
那把 SSH 连接断开 , 端口转发自然就失效了

那我们现在就可以检验一下是否生效了
使用 netstat 命令查看本地 8888 端口是否被绑定
image.png
那当我们配置了端口转发之后 ,如果之前配置过了端口转发 , 一定要断开之前的连接 , 重新连接才能生效
那我们后续 , 在 Java 代码中 , 通过 127.0.0.1:8888 就可以操作到我们的云服务器的 Redis 了 , 同时 , 外面的客户端是无法直接访问云服务器的 6379 端口的


我们再介绍一下 XTerminal 的配置方法
image.png
image.png
image.png

2.3 连接到 Redis 服务器

要想连接到 Redis 的服务器 , 我们需要使用 JedisPool 这样的对象 . 在 JedisPool 的构造方法中 , 就需要指定我们需要连接的服务器地址

注意服务器地址应该设置成本机的环回 IP + 我们配置的端口映射

import redis.clients.jedis.JedisPool;

public class RedisDemo {
    public static void main(String[] args) {
        // 连接到 Redis 服务器上
        JedisPool jedisPool = new JedisPool("tcp://127.0.0.1:8888");
        
    }
}

接下来我们就需要从 JedisPool 这个 Redis 连接池中取出一个连接 , 连接用完之后需要释放

所以我们可以直接选择 try-with-resources 这样的方式自动释放

import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;

public class RedisDemo {
    public static void main(String[] args) {
        // 连接到 Redis 服务器上
        JedisPool jedisPool = new JedisPool("tcp://127.0.0.1:8888");

        // 从 Redis 连接池中取出一个连接
        try (Jedis jedis = jedisPool.getResource()) {

        }
    }
}

那之后我们就可以使用 Redis 的各种命令了
我们以 Redis 中的 ping 命令来测试连接是否成功

import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;

public class RedisDemo {
    public static void main(String[] args) {
        // 连接到 Redis 服务器上
        JedisPool jedisPool = new JedisPool("tcp://127.0.0.1:8888");

        // 从 Redis 连接池中取出一个连接
        try (Jedis jedis = jedisPool.getResource()) {
            // Redis 的各种命令, 都可以对应到 Jedis 对象的各种方法
            
            // 验证 Redis 的连通性
            String pong = jedis.ping();
            System.out.println(pong);
        }
    }
}

注意 : 我们要确保云服务器的 Redis 的配置文件里面的配置是正确的
输入 cd /etc/redis/ , 然后用 vim 打开 redis.conf
image.png
判断这两个位置是否跟我的一致 , 不一致的话就要修改一下
image.png

控制台打印 PONG 代表连接成功
image.png

三 . 通用命令

3.1 set 和 get

我们先来模拟一下最基本的 get 和 set 的使用

import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;

public class RedisDemo {
    /**
     * 模拟 get 和 set 的使用
     */
    public static void test01(Jedis jedis) {
        System.out.println("模拟 get 和 set 的使用");

        // 避免上一组测试的残留数据影响到本组测试结果
        // 所以需要先清空一下数据库
        jedis.flushDB();

        // 模拟 set 的使用
        jedis.set("k1", "v1");
        jedis.set("k2", "v2");

        // 模拟 get 的使用
        String v1 = jedis.get("k1");
        String v2 = jedis.get("k2");
        System.out.println("value1 = " + v1);
        System.out.println("value2 = " + v2);
    }

    public static void main(String[] args) {
        // 连接到 Redis 服务器上
        JedisPool jedisPool = new JedisPool("tcp://127.0.0.1:8888");

        // 从 Redis 连接池中取出一个连接
        try (Jedis jedis = jedisPool.getResource()) {
            test01(jedis);
        }
    }
}

那 set 命令还有一些其他版本
image.png
我们可以看到 , set 方法还可以有第三个参数
那我们就可以先创建一个 SetParams 对象 , 然后调用 ex / nx / xx 等方法

import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.params.SetParams;

public class RedisDemo {
    /**
     * 模拟 get 和 set 的使用
     */
    public static void test01(Jedis jedis) {
        System.out.println("模拟 get 和 set 的使用");

        // 避免上一组测试的残留数据影响到本组测试结果
        // 所以需要先清空一下数据库
        jedis.flushDB();

        // 模拟 set 的使用
        jedis.set("k1", "v1");
        jedis.set("k2", "v2");

        // 模拟 get 的使用
        String v1 = jedis.get("k1");
        String v2 = jedis.get("k2");
        System.out.println("value1 = " + v1);
        System.out.println("value2 = " + v2);

        // set 的其它参数
        SetParams params = new SetParams();
        params.ex(10);// 设置超时时间为 10s
        params.xx();// 存在才去修改(更新)
        // params.nx();// 不存在才去创建
        jedis.set("k1", "v3", params);// 此时 k1 存在, 可以被修改
        System.out.println(jedis.get("k1"));
    }

    public static void main(String[] args) {
        // 连接到 Redis 服务器上
        JedisPool jedisPool = new JedisPool("tcp://127.0.0.1:8888");

        // 从 Redis 连接池中取出一个连接
        try (Jedis jedis = jedisPool.getResource()) {
            test01(jedis);
        }
    }
}

我们可以看一下运行结果
image.png

3.2 exists 和 del

import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.params.SetParams;

public class RedisDemo {
    /**
     * 模拟 exists 和 del 的使用
     */
    public static void test02(Jedis jedis) {
        System.out.println("模拟 exists 和 del 的使用");

        // 避免上一组测试的残留数据影响到本组测试结果
        // 所以需要先清空一下数据库
        jedis.flushDB();

        // 构造数据
        jedis.set("k1", "111");
        jedis.set("k2", "222");

        // 判断对应的 key 是否存在
        boolean result = jedis.exists("k1");
        System.out.println("k1 是否存在 : " + result);// true

        // 删除对应键值对
        // del 方法返回值为 long, 代表删除成功的个数
        long result2 = jedis.del("k2");
        System.out.println("删除成功的个数 : " + result2);// 1

        // 再去判断 k2 是否存在
        boolean result3 = jedis.exists("k2");
        System.out.println("k2 是否存在 : " + result3);// false

        // 可以删除多个 key
        // 但是仍然只会返回删除成功的个数
        long result4 = jedis.del("k1", "k2", "k3");
        System.out.println("删除成功的个数 : " + result4);// 1

    }

    public static void main(String[] args) {
        // 连接到 Redis 服务器上
        JedisPool jedisPool = new JedisPool("tcp://127.0.0.1:8888");

        // 从 Redis 连接池中取出一个连接
        try (Jedis jedis = jedisPool.getResource()) {
            test02(jedis);
        }
    }
}

3.3 keys

我们之前学习 keys 命令的时候 , 他有很多 pattern .
那我们先来看一下最基本的情况 : keys * , 查询所有的 key

import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.params.SetParams;

import java.util.Set;

public class RedisDemo {
    /**
     * 模拟 keys 的使用
     */
    public static void test03(Jedis jedis) {
        System.out.println("模拟 keys 的使用");

        // 避免上一组测试的残留数据影响到本组测试结果
        // 所以需要先清空一下数据库
        jedis.flushDB();

        // 构造数据
        jedis.set("k1", "111");
        jedis.set("k2", "222");
        jedis.set("k3", "333");
        jedis.set("k4", "444");

        // 模拟 keys 的使用
        // 返回值是 Set 类型
        Set<String> keys = jedis.keys("*");// 相当于 keys *
        System.out.println(keys);
    }
    
    public static void main(String[] args) {
        // 连接到 Redis 服务器上
        JedisPool jedisPool = new JedisPool("tcp://127.0.0.1:8888");

        // 从 Redis 连接池中取出一个连接
        try (Jedis jedis = jedisPool.getResource()) {
            test03(jedis);
        }
    }
}

运行结果 :
image.png
我们需要知道的是 , keys 命令的返回值是 Set 类型
image.png
之所以使用 Set 类型而不是 List 类型的原因是 : Redis 中要求 key 是不能重复的 , 并且对于 key 的先后顺序也并没有要求 , 所以使用 Set 类型会更合适一些

3.4 expire、ttl、type

我们先来看 ttl 和 expire 的使用方法

import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.params.SetParams;

import java.util.Set;

public class RedisDemo {

    /**
     * 模拟 expire、ttl 的使用
     */
    public static void test04(Jedis jedis) {
        System.out.println("模拟 expire、ttl、type 的使用");

        // 避免上一组测试的残留数据影响到本组测试结果
        // 所以需要先清空一下数据库
        jedis.flushDB();

        // 构造数据
        jedis.set("k1", "111");

        // 设置过期时间
        jedis.expire("k1", 10);// 设置过期时间为 10s

        // 获取剩余时间
        // ttl 返回值为 long
        long time = jedis.ttl("k1");
        System.out.println("当前键值对剩余时间为 " + time);
        // 等待 3s
        try {
            Thread.sleep(3000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        // 休眠 3s 之后继续获取剩余时间
        time = jedis.ttl("k1");
        System.out.println("当前键值对剩余时间为 " + time);
    }

    public static void main(String[] args) {
        // 连接到 Redis 服务器上
        JedisPool jedisPool = new JedisPool("tcp://127.0.0.1:8888");

        // 从 Redis 连接池中取出一个连接
        try (Jedis jedis = jedisPool.getResource()) {
            test04(jedis);
        }
    }
}

我们可以看一下运行结果
image.png
这就代表我们 expire 设置生效了 , 并且休眠 3s 之后还能正确获取剩余时

那接下来我们再来看 type 命令

import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.params.SetParams;

import java.util.Set;

public class RedisDemo {
    /**
     * 模拟 type 的使用
     */
    public static void test05(Jedis jedis) {
        System.out.println("模拟 type 的使用");

        // 避免上一组测试的残留数据影响到本组测试结果
        // 所以需要先清空一下数据库
        jedis.flushDB();

        // 构造数据
        jedis.set("k1", "111");// 设置成字符串类型
        String type1 = jedis.type("k1");
        System.out.println("当前数据的类型为: " + type1);

        jedis.lpush("k2","111","222","333");
        String type2 = jedis.type("k2");
        System.out.println("当前数据的类型为: " + type2);

        jedis.hset("k3","f1","111");
        String type3 = jedis.type("k3");
        System.out.println("当前数据的类型为: " + type3);

        jedis.sadd("k4","111","222","333");
        String type4 = jedis.type("k4");
        System.out.println("当前数据的类型为: " + type4);

        jedis.zadd("k5",10,"zhangsan");
        String type5 = jedis.type("k5");
        System.out.println("当前数据的类型为: " + type5);
    }

    public static void main(String[] args) {
        // 连接到 Redis 服务器上
        JedisPool jedisPool = new JedisPool("tcp://127.0.0.1:8888");

        // 从 Redis 连接池中取出一个连接
        try (Jedis jedis = jedisPool.getResource()) {
            test05(jedis);
        }
    }
}

我们可以看一下运行结果
image.png

三 . string 相关命令

我们给大家介绍这几个命令 :

  1. 针对多个 key 进行操作 : mset / mget
  2. 获取 / 设置字符串指定范围内容 : getrange / setrange
  3. 拼接字符串 : append
  4. 对数据进行自增自减 : incr / decr

3.1 mset / mget

注意 : mget 的返回值是 List , 而不是 Set
image.png

import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;

import java.util.List;

public class RedisDemoString {
    // mget mset 的用法
    public static void test1(Jedis jedis) {
        // 1. 清空数据库
        // 避免上一组的测试数据影响到下一组的测试结果
        jedis.flushDB();

        // 2. 设置多个键值对
        jedis.mset("key1", "value1", "key2", "value2", "key3", "value3");

        // 3. 获取多个键值对
        // 返回值: List
        List<String> values = jedis.mget("key1", "key2", "key3");
        System.out.println("values : " + values);
    }

    public static void main(String[] args) {
        // 1. 连接到 Redis 服务器上
        JedisPool jedisPool = new JedisPool("tcp://127.0.0.1:8888");

        // 2. 创建 Jedis 对象
        try (Jedis jedis = jedisPool.getResource()) {
            test1(jedis);
        }
    }
}

image.png
那我们的 value 与上面设置的 set 的顺序是一一对应的
那假如我们中间获取一个不存在的 key , 会发生什么 ?
image.png
如果获取不存在的值 , 就会打印 null , 这也说明了有几个参数 , 就有几个返回结果

3.2 getrange / setrange

import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;

import java.util.List;

public class RedisDemoString {
    // getrange / setrange 的使用
    public static void test2(Jedis jedis) {
        // 1. 清空数据库
        // 避免上一组的测试数据影响到下一组的测试结果
        jedis.flushDB();

        // 2. 添加数据
        jedis.set("key", "HelloWorld!");

        // 3. 获取指定区间的顺序
        // 左闭右闭区间
        String result = jedis.getrange("key", 2, 5);
        System.out.println("result = " + result);

        // 4. 设置指定区间的值
        jedis.setrange("key", 2, "666");
        System.out.println("修改之后的值: " + jedis.get("key"));
    }

    public static void main(String[] args) {
        // 1. 连接到 Redis 服务器上
        JedisPool jedisPool = new JedisPool("tcp://127.0.0.1:8888");

        // 2. 创建 Jedis 对象
        try (Jedis jedis = jedisPool.getResource()) {
            test2(jedis);
        }
    }
}

image.png

3.3 append

import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;

import java.util.List;

public class RedisDemoString {
    // append 的使用
    public static void test3(Jedis jedis) {
        // 1. 清空数据库
        // 避免上一组的测试数据影响到下一组的测试结果
        jedis.flushDB();

        // 2. 模拟数据
        jedis.set("key", "Hello");

        // 3. 追加数据
        jedis.append("key", "World");

        // 4. 查看追加后的数据
        System.out.println("修改之后的值 : " + jedis.get("key"));
    }

    public static void main(String[] args) {
        // 1. 连接到 Redis 服务器上
        JedisPool jedisPool = new JedisPool("tcp://127.0.0.1:8888");

        // 2. 创建 Jedis 对象
        try (Jedis jedis = jedisPool.getResource()) {
            test3(jedis);
        }
    }
}

image.png

3.4 incr / decr

import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;

import java.util.List;

public class RedisDemoString {
    // incr / decr 的使用
    public static void test4(Jedis jedis) {
        // 1. 清空数据库
        // 避免上一组的测试数据影响到下一组的测试结果
        jedis.flushDB();

        // 2. 模拟数据
        // 这次要模拟一个整数的数据
        jedis.set("key", "100");

        // 3. 自增操作
        // 返回自增之后的值
        long result1 = jedis.incr("key");
        System.out.println("自增操作返回的值: " + result1);// 预期是 101

        // 4. 获取 key 的值
        System.out.println("此时 key 的值为: " + jedis.get("key"));// 预期是 101

        // 5. 自减操作
        long result2 = jedis.decr("key");
        System.out.println("自减操作返回的值: " + result2);// 预期是 100

        // 6. 获取 key 的值
        System.out.println("此时 key 的值为: " + jedis.get("key"));// 预期是 100
    }

    public static void main(String[] args) {
        // 1. 连接到 Redis 服务器上
        JedisPool jedisPool = new JedisPool("tcp://127.0.0.1:8888");

        // 2. 创建 Jedis 对象
        try (Jedis jedis = jedisPool.getResource()) {
            test4(jedis);
        }
    }
}

image.png
那 incr 和 decr 还可以指定增加或者删除的值 , 使用 incrby 和 decrby

import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;

import java.util.List;

public class RedisDemoString {
    // incr / decr 的使用
    public static void test4(Jedis jedis) {
        // 1. 清空数据库
        // 避免上一组的测试数据影响到下一组的测试结果
        jedis.flushDB();

        // 2. 模拟数据
        // 这次要模拟一个整数的数据
        jedis.set("key", "100");

        // 3. 自增操作
        // 返回自增之后的值
        long result1 = jedis.incr("key");
        System.out.println("自增操作返回的值: " + result1);// 预期是 101

        // 4. 获取 key 的值
        System.out.println("此时 key 的值为: " + jedis.get("key"));// 预期是 101

        // 5. 自减操作
        long result2 = jedis.decr("key");
        System.out.println("自减操作返回的值: " + result2);// 预期是 100

        // 6. 获取 key 的值
        System.out.println("此时 key 的值为: " + jedis.get("key"));// 预期是 100

        // 7. 指定增加的值
        long result3 = jedis.incrBy("key", 10);
        System.out.println("指定增加的值最后变成了: " + result3);// 预期是 110

        // 8. 获取 key 的值
        System.out.println("此时 key 的值为: " + jedis.get("key"));// 预期是 110

        // 9. 指定减少的值
        long result4 = jedis.decrBy("key", 5);
        System.out.println("指定减少的值最后变成了: " + result4);// 预期是 105

        // 10. 获取 key 的值
        System.out.println("此时 key 的值为: " + jedis.get("key"));// 预期是 105
    }

    public static void main(String[] args) {
        // 1. 连接到 Redis 服务器上
        JedisPool jedisPool = new JedisPool("tcp://127.0.0.1:8888");

        // 2. 创建 Jedis 对象
        try (Jedis jedis = jedisPool.getResource()) {
            test4(jedis);
        }
    }
}

image.png

3.5 mset、mget

我们先来看 mset 和 mget 这一组
mset 可以设置多组键值对 , mget 可以获取多组键值对

import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;

import java.util.List;

public class StringDemo {
    public static void main(String[] args) {
        JedisPool jedisPool = new JedisPool("tcp://127.0.0.1:8888");
        try (Jedis jedis = jedisPool.getResource()) {
            test01(jedis);
        }
    }

    private static void test01(Jedis jedis) {
        System.out.println("模拟 mset mget 的使用");

        // 避免上一组测试的残留数据影响到本组测试结果
        // 所以需要先清空一下数据库
        jedis.flushDB();

        // 模拟 mset 设置多组数据
        jedis.mset("k1","v1","k2","v2","k3","v3");

        // 模拟 mget 获取多组数据
        List<String> values1 = jedis.mget("k1","k2","k3");
        System.out.println("value 的值为 : " + values1);
    }
}

那我们来看一下运行结果
image.png
那要注意的是 , mget 的返回值是 List 而不是 Set
image.png

之前学习 keys 命令的时候 , 他的返回值是 Set , 是因为 key 要求不能重复 . 而我们的 value 是可以重复的 , 所以使用 List 来去接收

那如果我们获取的多个键值对中某个 key 不存在 , 该怎样返回呢 ?

import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;

import java.util.List;

public class StringDemo {
    public static void main(String[] args) {
        JedisPool jedisPool = new JedisPool("tcp://127.0.0.1:8888");
        try (Jedis jedis = jedisPool.getResource()) {
            test01(jedis);
        }
    }

    private static void test01(Jedis jedis) {
        System.out.println("模拟 mset mget 的使用");

        // 避免上一组测试的残留数据影响到本组测试结果
        // 所以需要先清空一下数据库
        jedis.flushDB();

        // 模拟 mset 设置多组数据
        jedis.mset("k1","v1","k2","v2","k3","v3");

        // 模拟 mget 获取多组数据
        List<String> values1 = jedis.mget("k1","k2","k3");
        System.out.println("value 的值为 : " + values1);

        // 如果某个 key 在 Redis 中不存在
        // 则对应的位置用 null 表示
        List<String> values2 = jedis.mget("k1","k2","k100","k3");
        System.out.println("value 的值为 : " + values2);
    }
}

我们可以通过运行结果来看一下
image.png
那我们就知道了 , 如果某个 key 在 Redis 上不存在 , 则对应的 value 用 null 来表示

那我们再来看一下 getrange 和 setrange
getrange 的作用是获取到 [start , end] 区间内的数据
setrange 的作用是从 start 位置开始 , 修改内容为指定的字符串

import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;

import java.util.List;

public class StringDemo {
    public static void main(String[] args) {
        JedisPool jedisPool = new JedisPool("tcp://127.0.0.1:8888");
        try (Jedis jedis = jedisPool.getResource()) {
            test02(jedis);
        }
    }

    private static void test02(Jedis jedis) {
        System.out.println("模拟 getrange 和 setrange 的使用");

        // 避免上一组测试的残留数据影响到本组测试结果
        // 所以需要先清空一下数据库
        jedis.flushDB();

        // 构造测试数据
        jedis.set("key","abcdefghigklmn");

        // 获取 [start,end] 区间的数据
        String str1 = jedis.getrange("key",2,5);
        System.out.println(str1);

        // 从 start 位置开始, 修改内容为指定的字符串
        // 从下标为 2 的位置开始修改, 修改成 hahaha
        jedis.setrange("key",2,"hahaha");
        String str2 = jedis.get("key");
        System.out.println(str2);
    }
}

我们可以来看一下运行结果
image.png

3.6 append、incr、decr

append 的作用是拼接字符串

import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;

import java.util.List;

public class StringDemo {
    public static void main(String[] args) {
        JedisPool jedisPool = new JedisPool("tcp://127.0.0.1:8888");
        try (Jedis jedis = jedisPool.getResource()) {
            test03(jedis);
        }
    }

    private static void test03(Jedis jedis) {
        System.out.println("模拟 append 的使用");

        // 避免上一组测试的残留数据影响到本组测试结果
        // 所以需要先清空一下数据库
        jedis.flushDB();

        // 构造数据
        jedis.set("key", "Hello");

        // 拼接字符串
        jedis.append("key", "World");

        String value = jedis.get("key");
        System.out.println("value = " + value);
    }
}

我们可以看一下运行结果
image.png

我们再来看一下 incr 和 decr 的用法

import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;

import java.util.List;

public class StringDemo {
    public static void main(String[] args) {
        JedisPool jedisPool = new JedisPool("tcp://127.0.0.1:8888");
        try (Jedis jedis = jedisPool.getResource()) {
            test04(jedis);
        }
    }

    private static void test04(Jedis jedis) {
        System.out.println("模拟 incr 和 decr 的使用");

        // 避免上一组测试的残留数据影响到本组测试结果
        // 所以需要先清空一下数据库
        jedis.flushDB();

        // 构造数据
        jedis.set("key", "100");

        // 模拟 incr 的使用
        // incr 返回值类型为 long, 代表自增后的结果
        long result1 = jedis.incr("key");
        System.out.println("自增之后的值为 : " + result1);

        String value1 = jedis.get("key");
        System.out.println("value 的值为 : " + value1);

        // 模拟 decr 的使用
        // decr 返回值类型为 long, 代表自减后的结果
        long result2 = jedis.decr("key");
        System.out.println("自增之后的值为 : " + result2);

        String value2 = jedis.get("key");
        System.out.println("value 的值为 : " + value2);
    }
}

image.png
还有其他的比如 incrby、incrbyfloat 命令 , 我们就不进行演示了

四 . list 类型

4.1 lpush、lrange、rpush

lpush 的作用是向列表中添加元素 (头插法)
lrange 的作用是可以获取到列表中的元素
rpush 就相当于尾插法

import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;

import java.util.List;

public class ListDemo {
    public static void main(String[] args) {
        JedisPool jedisPool = new JedisPool("tcp://127.0.0.1:8888");
        try (Jedis jedis = jedisPool.getResource()) {
            test01(jedis);
        }
    }

    private static void test01(Jedis jedis) {
        System.out.println("模拟 lpush 和 lrange 的使用");

        // 避免上一组测试的残留数据影响到本组测试结果
        // 所以需要先清空一下数据库
        jedis.flushDB();

        // 头插法向列表中插入元素
        // 返回值为插入成功之后 list 的长度
        jedis.lpush("key", "111", "222", "333");

        // 获取列表中的元素
        // 返回值为 List
        List<String> result1 = jedis.lrange("key", 0, -1);
        System.out.println("头插法之后的列表为 : " + result1);

        // 尾插法向列表中插入元素
        jedis.rpush("key", "555", "666");
        List<String> result2 = jedis.lrange("key", 0, -1);
        System.out.println("尾插法之后的列表为 : " + result2);
    }
}

我们可以看一下运行结果
image.png

4.2 lpop、rpop

lpop 就相当头头删法
rpop 就相当于尾删法

import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;

import java.util.List;

public class ListDemo {
    public static void main(String[] args) {
        JedisPool jedisPool = new JedisPool("tcp://127.0.0.1:8888");
        try (Jedis jedis = jedisPool.getResource()) {
            test02(jedis);
        }
    }

    private static void test02(Jedis jedis) {
        System.out.println("模拟 lpush 和 lrange 的使用");

        // 避免上一组测试的残留数据影响到本组测试结果
        // 所以需要先清空一下数据库
        jedis.flushDB();

        // 构造列表中的数据
        jedis.rpush("key", "111", "222", "333", "444");

        // 从列表尾部删除元素
        String result1 = jedis.rpop("key");
        System.out.println("尾删法删除的值为 : " + result1);
        result1 = jedis.rpop("key");
        System.out.println("尾删法删除的值为 : " + result1);

        // 从列表头部删除元素
        String result2 = jedis.lpop("key");
        System.out.println("头删法删除的值为 : " + result2);
        result2 = jedis.lpop("key");
        System.out.println("头删法删除的值为 : " + result2);

        // 此时列表为空
        // 继续获取就会获取到 null
        result1 = jedis.rpop("key");
        System.out.println("尾删法删除的值为 : " + result1);
        result2 = jedis.lpop("key");
        System.out.println("头删法删除的值为 : " + result2);
    }
}

image.png

4.3 blpop 和 brpop

blpop 和 brpop 就相当于阻塞版本的 lpop 和 rpop

import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;

import java.util.List;

public class ListDemo {
    public static void main(String[] args) {
        JedisPool jedisPool = new JedisPool("tcp://127.0.0.1:8888");
        try (Jedis jedis = jedisPool.getResource()) {
            test03(jedis);
        }
    }

    private static void test03(Jedis jedis) {
        System.out.println("模拟 blpop 的使用");

        // 避免上一组测试的残留数据影响到本组测试结果
        // 所以需要先清空一下数据库
        jedis.flushDB();

        // 我们先在列表为空的时候去执行以下 blpop
        // 第一个参数: 超时时间(单位: s)
        // 第二个参数: 要获取的 key
        // 返回值是一个二元组, 使用 List 接收
        // 一个是从哪个 key 获取到的结果, 一个是删除的元素是什么
        List<String> result = jedis.blpop(3, "key");
        System.out.println("从 " + result.get(0) + " 中获取到的结果");
        System.out.println("删除的元素为 : " + result.get(1));
    }
}

此时由于我们的 Redis 中并没有数据 , 所以 blpop 就会发生阻塞等待 , 超过设置的时间就会返回 null 导致代码出现空指针异常
image.png
那我们可以模拟一下其他客户端插入元素的情况
模拟 blpop 阻塞.gif

4.4 llen

llen 的作用是获取到列表的元素个数

import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;

import java.util.List;

public class ListDemo {
    public static void main(String[] args) {
        JedisPool jedisPool = new JedisPool("tcp://127.0.0.1:8888");
        try (Jedis jedis = jedisPool.getResource()) {
            test04(jedis);
        }
    }

    private static void test04(Jedis jedis) {
        System.out.println("模拟 llen 的使用");

        // 避免上一组测试的残留数据影响到本组测试结果
        // 所以需要先清空一下数据库
        jedis.flushDB();

        // 构造列表中的数据
        jedis.rpush("key", "111", "222", "333", "444");

        // 获取列表中的元素个数
        long len = jedis.llen("key");
        System.out.println("列表中的元素个数为 " + len);
    }
}

image.png

五 . set 类型

5.1 sadd、smembers

sadd 的作用是向集合中添加元素
smembers 是获取到集合中的元素

import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;

import java.util.Set;

public class SetDemo {
    public static void main(String[] args) {
        JedisPool jedisPool = new JedisPool("tcp://127.0.0.1:8888");

        try (Jedis jedis = jedisPool.getResource()) {
            test01(jedis);
        }
    }

    private static void test01(Jedis jedis) {
        System.out.println("模拟 sadd、smembers 的使用");

        // 避免上一组测试的残留数据影响到本组测试结果
        // 所以需要先清空一下数据库
        jedis.flushDB();

        // 向集合中添加元素
        jedis.sadd("key", "111", "222", "333", "444");

        // 获取集合中的元素
        // smembers 返回值是一个 Set
        // 注意: smembers 顺序是无序的, 与插入顺序无关
        Set<String> result = jedis.smembers("key");
        System.out.println("集合中的元素为 : " + result);
    }
}

image.png

5.2 sismember

sismember 的作用是判断元素是否在集合中存在

import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;

import java.util.Set;

public class SetDemo {
    public static void main(String[] args) {
        JedisPool jedisPool = new JedisPool("tcp://127.0.0.1:8888");

        try (Jedis jedis = jedisPool.getResource()) {
            test02(jedis);
        }
    }

    private static void test02(Jedis jedis) {
        System.out.println("模拟 sismember 的使用");

        // 避免上一组测试的残留数据影响到本组测试结果
        // 所以需要先清空一下数据库
        jedis.flushDB();

        // 向集合中添加元素
        jedis.sadd("key", "111", "222", "333", "444");

        // 判断元素是否存在
        // sismember 返回值为布尔类型
        boolean result1 = jedis.sismember("key", "111");
        System.out.println(result1);
        // key 不存在就会返回 false
        boolean result2 = jedis.sismember("key100", "111");
        System.out.println(result2);
        // value 不存在也会返回 false
        boolean result3 = jedis.sismember("key", "666");
        System.out.println(result3);
    }

}

image.png

5.3 scard

scard 是用来获取集合中的元素个数

import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;

import java.util.Set;

public class SetDemo {
    public static void main(String[] args) {
        JedisPool jedisPool = new JedisPool("tcp://127.0.0.1:8888");

        try (Jedis jedis = jedisPool.getResource()) {
            test03(jedis);
        }
    }

    private static void test03(Jedis jedis) {
        System.out.println("模拟 scard 的使用");

        // 避免上一组测试的残留数据影响到本组测试结果
        // 所以需要先清空一下数据库
        jedis.flushDB();

        // 向集合中添加元素
        jedis.sadd("key", "111", "222", "333", "444");

        // 获取集合中的元素个数
        long len = jedis.scard("key");
        System.out.println("集合中的元素个数为 : " + len);
    }
}

image.png

5.4 spop

spop 的作用是随机删除一个元素

import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;

import java.util.Set;

public class SetDemo {
    public static void main(String[] args) {
        JedisPool jedisPool = new JedisPool("tcp://127.0.0.1:8888");

        try (Jedis jedis = jedisPool.getResource()) {
            test04(jedis);
        }
    }

    private static void test04(Jedis jedis) {
        System.out.println("模拟 spop 的使用");

        // 避免上一组测试的残留数据影响到本组测试结果
        // 所以需要先清空一下数据库
        jedis.flushDB();

        // 向集合中添加元素
        jedis.sadd("key", "111", "222", "333", "444");

        // spop 的作用是随机删除一个元素
        // 返回值是被删除的元素
        String result = jedis.spop("key");
        System.out.println("随机删除的值为 : " + result);
    }
}

我们可以多次运行来看一下删除的值是否是固定的
spop 随机删除元素.gif

5.5 sinter、sinterstore

sinter 和 sinterstore 的作用是用来求多个集合的交集

import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;

import java.util.Set;

public class SetDemo {
    public static void main(String[] args) {
        JedisPool jedisPool = new JedisPool("tcp://127.0.0.1:8888");

        try (Jedis jedis = jedisPool.getResource()) {
            test05(jedis);
        }
    }

    private static void test05(Jedis jedis) {
        System.out.println("模拟 sinter、sinterstore 的使用");

        // 避免上一组测试的残留数据影响到本组测试结果
        // 所以需要先清空一下数据库
        jedis.flushDB();

        // 向集合中添加元素
        jedis.sadd("key1", "111", "222", "333");
        jedis.sadd("key2", "111", "222", "444");

        // 求多个集合的交集
        Set<String> result1 = jedis.sinter("key1", "key2");
        System.out.println("sinter 交集的结果为 : " + result1);

        // 将交集的结果保存到另外的 key 中
        // sinterstore 的返回值表示交集的个数
        long len = jedis.sinterstore("dest", "key1", "key2");
        System.out.println("sinterstore 交集的长度为 : " + len);
        Set<String> result2 = jedis.smembers("dest");
        System.out.println("sinterstore 交集的结果为 : " + result2);
    }
}

image.png

六 . hash 类型

6.1 hset、hget

hset 的作用是向哈希中添加元素
hget 是用来获取哈希中的元素的
我们先来看一下 hset
image.png
他有两种版本 , 一种是正常添加 key-field-value , 另一种是将多组 field-value 打包成一个 Map 进行插入
我们分别来演示

import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;

import java.util.HashMap;
import java.util.Map;

public class HashDemo {
    public static void main(String[] args) {
        JedisPool jedisPool = new JedisPool("tcp://127.0.0.1:8888");

        try (Jedis jedis = jedisPool.getResource()) {
            test01(jedis);
        }
    }

    private static void test01(Jedis jedis) {
        System.out.println("模拟 hset 和 hget 的使用");

        // 避免上一组测试的残留数据影响到本组测试结果
        // 所以需要先清空一下数据库
        jedis.flushDB();

        // 向哈希中添加一组元素
        jedis.hset("k1", "f1", "111");

        // 向哈希中添加多组元素
        // 将多组 field-value 打包成一个 Map
        Map<String, String> fields = new HashMap<>();
        fields.put("f2", "222");
        fields.put("f3", "333");
        jedis.hset("k1", fields);

        // 获取哈希中的元素
        String result1 = jedis.hget("k1", "f1");
        System.out.println("哈希中的元素 : " + result1);
        // 如果 key 不存在, 就会获取失败
        String result2 = jedis.hget("k100", "f1");
        System.out.println("哈希中的元素 : " + result2);
        // 如果 field 不存在, 就会获取失败
        String result3 = jedis.hget("k1", "f100");
        System.out.println("哈希中的元素 : " + result3);
    }
}

image.png

6.2 hexists

hexists 的作用是用来判断元素是否存在

import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;

import java.util.HashMap;
import java.util.Map;

public class HashDemo {
    public static void main(String[] args) {
        JedisPool jedisPool = new JedisPool("tcp://127.0.0.1:8888");

        try (Jedis jedis = jedisPool.getResource()) {
            test02(jedis);
        }
    }

    private static void test02(Jedis jedis) {
        System.out.println("模拟 hexists 的使用");

        // 避免上一组测试的残留数据影响到本组测试结果
        // 所以需要先清空一下数据库
        jedis.flushDB();

        // 向哈希中添加多组元素
        // 将多组 field-value 打包成一个 Map
        Map<String, String> fields = new HashMap<>();
        fields.put("f1", "111");
        fields.put("f2", "222");
        fields.put("f3", "333");
        jedis.hset("k1", fields);

        // 判断某个元素是否存在
        // 返回值是布尔类型
        boolean result1 = jedis.hexists("k1", "f1");
        System.out.println("该元素是否存在 : " + result1);

        // 如果 key 不存在, 就会返回 false
        boolean result2 = jedis.hexists("k100", "f1");
        System.out.println("该元素是否存在 : " + result2);
        // 如果 field 不存在, 就会返回 false
        boolean result3 = jedis.hexists("k1", "f100");
        System.out.println("该元素是否存在 : " + result3);
    }
}

image.png

6.3 hdel

hedel 的作用是删除指定的 field

import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;

import java.util.HashMap;
import java.util.Map;

public class HashDemo {
    public static void main(String[] args) {
        JedisPool jedisPool = new JedisPool("tcp://127.0.0.1:8888");

        try (Jedis jedis = jedisPool.getResource()) {
            test03(jedis);
        }
    }

    private static void test03(Jedis jedis) {
        System.out.println("模拟 hdel 的使用");

        // 避免上一组测试的残留数据影响到本组测试结果
        // 所以需要先清空一下数据库
        jedis.flushDB();

        // 向哈希中添加多组元素
        // 将多组 field-value 打包成一个 Map
        Map<String, String> fields = new HashMap<>();
        fields.put("f1", "111");
        fields.put("f2", "222");
        fields.put("f3", "333");
        jedis.hset("k1", fields);

        // hdel 的作用是删除指定的 key 下面的 field
        // 返回值代表删除成功的个数
        long delNum = jedis.hdel("k1", "f1", "f2", "f100");
        System.out.println("删除成功的个数为 : " + delNum);

        // 此时再去判断 f1 和 f2 是否还存在
        boolean result1 = jedis.hexists("k1", "f1");
        System.out.println("f1 是否存在 : " + result1);
        boolean result2 = jedis.hexists("k1", "f2");
        System.out.println("f2 是否存在 : " + result2);
    }
}

image.png

6.4 hkeys、hvals

hkeys 是用来获取哈希中所有的 key
hvals 是用来获取海中所有的 val

import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;

import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;

public class HashDemo {
    public static void main(String[] args) {
        JedisPool jedisPool = new JedisPool("tcp://127.0.0.1:8888");

        try (Jedis jedis = jedisPool.getResource()) {
            test04(jedis);
        }
    }

    private static void test04(Jedis jedis) {
        System.out.println("模拟 hkeys、hvals 的使用");

        // 避免上一组测试的残留数据影响到本组测试结果
        // 所以需要先清空一下数据库
        jedis.flushDB();

        // 向哈希中添加多组元素
        // 将多组 field-value 打包成一个 Map
        Map<String, String> fields = new HashMap<>();
        fields.put("f1", "111");
        fields.put("f2", "222");
        fields.put("f3", "333");
        jedis.hset("k1", fields);

        // hkeys 用来获取所有的 field
        // 返回值是 Set 类型
        Set<String> result1 = jedis.hkeys("k1");
        System.out.println("keys : " + result1);

        // hvals 用来获取所有的 value
        // 返回值是 List 类型
        List<String> result2 = jedis.hvals("k1");
        System.out.println("vals : " + result2);
    }
}

image.png
那要注意的是 , hkeys 的返回值类型为 Set , hvals 的返回值类型为 List

这是因为哈希中要求 key 不能重复 , 所以使用 Set 来去接收 ; 而 value 是可以重复的 , 所以使用 List 来去接收

6.5 hmset、hmget

hmset 的作用与 hset 差不多 , 都是用来添加哈希的 , 并且添加多组哈希都需要用到 Map 辅助
hmget 的作用是获取多组哈希的值

import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;

import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;

public class HashDemo {
    public static void main(String[] args) {
        JedisPool jedisPool = new JedisPool("tcp://127.0.0.1:8888");

        try (Jedis jedis = jedisPool.getResource()) {
            test05(jedis);
        }
    }

    private static void test05(Jedis jedis) {
        System.out.println("模拟 hmset、hmget 的使用");

        // 避免上一组测试的残留数据影响到本组测试结果
        // 所以需要先清空一下数据库
        jedis.flushDB();

        // hmset 可以添加多组哈希
        // 但仍然需要将多组 field-value 打包成一个 Map
        Map<String, String> map = new HashMap<>();
        map.put("f1", "111");
        map.put("f2", "222");
        map.put("f3", "333");
        jedis.hmset("k1", map);

        // hmget 可以获取多组哈希
        List<String> values = jedis.hmget("k1", "f1", "f2", "f3", "f4");
        System.out.println("values : " + values);
    }
}

image.png
那他的特点就是会根据 field 的顺序来去打印 value 的顺序 , 也就是说 field-value 是严格对应的

hkeys 和 hvals 则不是 , 他们都是随机打印的 , 并不能一一对应

image.png

七 . zset 类型

7.1 zadd、zrange

zadd 是向有序集合中添加元素
zrange 是用来获取有序集合的所有元素

import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;

import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class ZSetDemo {
    public static void main(String[] args) {
        JedisPool jedisPool = new JedisPool("tcp://127.0.0.1:8888");

        try (Jedis jedis = jedisPool.getResource()) {
            test01(jedis);
        }
    }

    private static void test01(Jedis jedis) {
        System.out.println("模拟 zadd、zrange 的使用");

        // 避免上一组测试的残留数据影响到本组测试结果
        // 所以需要先清空一下数据库
        jedis.flushDB();

        // 向 zset 中添加一组元素
        jedis.zadd("key", 10.0, "zhangsan");

        // 也可以向 zset 中添加多组元素
        // 但是也需要借助 Map 来去设置 score 和 value
        Map<String, Double> map = new HashMap<>();
        map.put("lisi", 20.0);
        map.put("wangwu", 30.0);
        jedis.zadd("key", map);

        // 获取 zset 中的元素
        // 返回值类型为 List
        List<String> members = jedis.zrange("key", 0, -1);
        System.out.println("有序集合中的元素为 : " + members);
    }
}

image.png
如果我们想既获取到 members , 又获取到 scores , 那就需要使用 zrangewithscores 这个方法了
但是 zrangewithscores 的返回值类型有些特殊 , 需要我们留意一下
image.png
那这个 Tuple 是 Jedis 提供给我们的数据类型 , 代表元组 (也就是一组数据)

import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.resps.Tuple;

import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class ZSetDemo {
    public static void main(String[] args) {
        JedisPool jedisPool = new JedisPool("tcp://127.0.0.1:8888");

        try (Jedis jedis = jedisPool.getResource()) {
            test01(jedis);
        }
    }

    private static void test01(Jedis jedis) {
        System.out.println("模拟 zadd、zrange 的使用");

        // 避免上一组测试的残留数据影响到本组测试结果
        // 所以需要先清空一下数据库
        jedis.flushDB();

        // 向 zset 中添加一组元素
        jedis.zadd("key", 10.0, "zhangsan");

        // 也可以向 zset 中添加多组元素
        // 但是也需要借助 Map 来去设置 score 和 value
        Map<String, Double> map = new HashMap<>();
        map.put("lisi", 20.0);
        map.put("wangwu", 30.0);
        jedis.zadd("key", map);

        // 获取 zset 中的元素
        List<String> members = jedis.zrange("key", 0, -1);
        System.out.println("有序集合中的元素为 : " + members);

        // 既获取 members, 又获取 scores
        List<Tuple> memberswithscores = jedis.zrangeWithScores("key", 0, -1);
        System.out.println("有序集合中的元素为 : " + memberswithscores);

        // 获取其中某一个元素
        String member = memberswithscores.get(0).getElement();
        double score = memberswithscores.get(0).getScore();
        System.out.println("第一个元素 : " + member + " , 他的成绩为 : " + score);
    }
}

image.png

7.2 zcard

zcard 是用来统计有序集合的元素个数的

import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.resps.Tuple;

import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class ZSetDemo {
    public static void main(String[] args) {
        JedisPool jedisPool = new JedisPool("tcp://127.0.0.1:8888");

        try (Jedis jedis = jedisPool.getResource()) {
            test02(jedis);
        }
    }

    private static void test02(Jedis jedis) {
        System.out.println("模拟 zcard 的使用");

        // 避免上一组测试的残留数据影响到本组测试结果
        // 所以需要先清空一下数据库
        jedis.flushDB();

        // 构造多组有序集合数据
        jedis.zadd("key", 10, "zhangsan");
        jedis.zadd("key", 20, "lisi");
        jedis.zadd("key", 30, "wangwu");

        // 获取有序集合的元素个数
        long len = jedis.zcard("key");
        System.out.println("有序集合的元素个数为 : " + len);
    }
}

image.png

7.3 zrem

zrem 用来删除指定元素

import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.resps.Tuple;

import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class ZSetDemo {
    public static void main(String[] args) {
        JedisPool jedisPool = new JedisPool("tcp://127.0.0.1:8888");

        try (Jedis jedis = jedisPool.getResource()) {
            test03(jedis);
        }
    }

    private static void test03(Jedis jedis) {
        System.out.println("模拟 zrem 的使用");

        // 避免上一组测试的残留数据影响到本组测试结果
        // 所以需要先清空一下数据库
        jedis.flushDB();

        // 构造多组有序集合数据
        jedis.zadd("key", 10, "zhangsan");
        jedis.zadd("key", 20, "lisi");
        jedis.zadd("key", 30, "wangwu");

        // 删除指定元素
        // 可以删除多组数据, 返回值代表删除成功的个数
        long remNum = jedis.zrem("key", "zhangsan", "lisi");
        System.out.println("删除成功的元素个数 : " + remNum);

        // 获取剩余元素
        List<Tuple> result = jedis.zrangeWithScores("key", 0, -1);
        System.out.println("剩余元素 : " + result);
    }
}

image.png

7.4 zscore

zscore 的作用是根据 member 来去找到对应的 score

import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.resps.Tuple;

import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class ZSetDemo {
    public static void main(String[] args) {
        JedisPool jedisPool = new JedisPool("tcp://127.0.0.1:8888");

        try (Jedis jedis = jedisPool.getResource()) {
            test04(jedis);
        }
    }

    private static void test04(Jedis jedis) {
        System.out.println("模拟 zscore 的使用");

        // 避免上一组测试的残留数据影响到本组测试结果
        // 所以需要先清空一下数据库
        jedis.flushDB();

        // 构造多组有序集合数据
        jedis.zadd("key", 10, "zhangsan");
        jedis.zadd("key", 20, "lisi");
        jedis.zadd("key", 30, "wangwu");

        // 根据 member 获取对应的分数
        double score1 = jedis.zscore("key", "zhangsan");
        System.out.println("score = " + score1);
    }
}

image.png
但是有一个需要注意的位置 : 就是我们在接收 score 的时候 , 使用的是 double 来去接收的
如果 member 存在 , 那么正常返回成绩 , 没什么问题 ; 但是如果 member 不存在呢 ?
member 不存在 , zscore 就会返回 null , 用 double 去接收 null 是会触发空指针异常的 .
image.png
所以我们尽量谁用包装类来去接收

import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.resps.Tuple;

import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class ZSetDemo {
    public static void main(String[] args) {
        JedisPool jedisPool = new JedisPool("tcp://127.0.0.1:8888");

        try (Jedis jedis = jedisPool.getResource()) {
            test04(jedis);
        }
    }

    private static void test04(Jedis jedis) {
        System.out.println("模拟 zscore 的使用");

        // 避免上一组测试的残留数据影响到本组测试结果
        // 所以需要先清空一下数据库
        jedis.flushDB();

        // 构造多组有序集合数据
        jedis.zadd("key", 10, "zhangsan");
        jedis.zadd("key", 20, "lisi");
        jedis.zadd("key", 30, "wangwu");

        // 根据 member 获取对应的分数
        // 注意: 请使用包装类进行接收, 避免触发空指针异常
        Double score1 = jedis.zscore("key", "zhangsan");
        System.out.println("score = " + score1);
    }
}

image.png

7.5 zrank

zrank 的作用是获取到某个 member 他所对应的排名 (下标)

import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.resps.Tuple;

import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class ZSetDemo {
    public static void main(String[] args) {
        JedisPool jedisPool = new JedisPool("tcp://127.0.0.1:8888");

        try (Jedis jedis = jedisPool.getResource()) {
            test05(jedis);
        }
    }
    
    private static void test05(Jedis jedis) {
        System.out.println("模拟 zrank 的使用");

        // 避免上一组测试的残留数据影响到本组测试结果
        // 所以需要先清空一下数据库
        jedis.flushDB();

        // 构造多组有序集合数据
        jedis.zadd("key", 10, "zhangsan");
        jedis.zadd("key", 20, "lisi");
        jedis.zadd("key", 30, "wangwu");

        // 获取某个 member 的排名
        // 注意: 返回值用 Long 包装类接收, 避免空指针异常
        Long rank = jedis.zrank("key", "lisi");
        System.out.println("rank : " + rank);
    }
}

image.png


这篇文章知识含量超级大 , 如果对大家有帮助的话 , 还请一键三连~
在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2105815.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

一文说清什么是数据仓库

01 数据仓库的概念 数据仓库的概念可以追溯到20世纪80年代&#xff0c;当时IBM的研究人员开发出了“商业数据仓库”。本质上&#xff0c;数据仓库试图提供一种从操作型系统到决策支持环境的数据流架构模型。 目前对数据仓库&#xff08;Data Warehouse&#xff09;的标准定义&a…

高级组件封装技巧--tree的封装

el-tree是一个经常用到的组件&#xff0c;但是它不支持v-model,使用起来很麻烦&#xff0c;这篇教程封装了el-tree,使得它使用起来很简单&#xff0c;并且支持搜索&#xff0c;支持叶子节点横向排列&#xff0c;这样就算数据多了&#xff0c;也会显的很紧凑,同时它支持提交half…

springboot+mybatis+vue2分页功能开发

前端框架代码 <div class"block"><span class"demonstration">完整功能</span><el-paginationsize-change"handleSizeChange"current-change"handleCurrentChange":current-page"currentPage4":page-s…

MLM:多模态大型语言模型的简介、微调方法、发展历史及其代表性模型、案例应用之详细攻略

MLM&#xff1a;多模态大型语言模型的简介、微调方法、发展历史及其代表性模型、案例应用之详细攻略 目录 相关文章 AI之MLM&#xff1a;《MM-LLMs: Recent Advances in MultiModal Large Language Models多模态大语言模型的最新进展》翻译与解读 MLM之CLIP&#xff1a;CLIP…

Draw.io for Mac/Win:免费且强大的流程图绘制工具

在数字化时代&#xff0c;流程图已成为表达复杂过程和逻辑关系的重要工具。Draw.io&#xff08;现也称为diagrams.net&#xff09;&#xff0c;作为一款免费且功能强大的流程图绘制工具&#xff0c;无论是对于Mac还是Windows用户&#xff0c;都是不可多得的选择。 一、跨平台兼…

计算机毕业设计 半成品配菜平台 Java+SpringBoot+Vue 前后端分离 文档报告 代码讲解 安装调试

&#x1f34a;作者&#xff1a;计算机编程-吉哥 &#x1f34a;简介&#xff1a;专业从事JavaWeb程序开发&#xff0c;微信小程序开发&#xff0c;定制化项目、 源码、代码讲解、文档撰写、ppt制作。做自己喜欢的事&#xff0c;生活就是快乐的。 &#x1f34a;心愿&#xff1a;点…

智能电网中巡检机器人的信息安全技术应用

随着电力行业的数字化和智能化发展&#xff0c;智能巡检机器人作为电力系统自动化运维的重要工具&#xff0c;能够在无人干预的情况下&#xff0c;对电网设备进行实时监测和故障诊断。这种高效、可靠的巡检方式在智能电网建设中发挥了重要作用。然而&#xff0c;随着机器人在电…

yolo 3d车辆目标检测(教程+代码)

关于3D目标检测及其与YOLO3D相关性的概览&#xff1a; 3D目标检测&#xff1a;开启视觉感知的新维度 随着计算机视觉技术的发展&#xff0c;目标检测算法已经成为人工智能领域的重要组成部分。从自动驾驶汽车到无人机导航&#xff0c;再到增强现实&#xff08;AR&#xff09;应…

Java项目:137 springboot基于springboot的智能家居系统

作者主页&#xff1a;源码空间codegym 简介&#xff1a;Java领域优质创作者、Java项目、学习资料、技术互助 文中获取源码 项目介绍 本基于Springboot的智能家居系统提供管理员、用户两种角色的服务。 总的功能个人中心、基础数据管理、家具管理、任务管理和用户管理。本系统…

计算机工具软件安装攻略:Visual Studio Code下载、安装和使用

Visual Studio Code下载、安装和使用 1 Visual Studio Code简介 Visual Studio Code通常简称为VS Code&#xff0c;是一款由微软开发的免费、开源的轻量级代码编辑器。它在开发者社区中非常受欢迎&#xff0c;具有强大的功能和扩展性&#xff0c;适用于多种编程语言和开发场景…

git如何设置嵌套仓库(设置子树或子模块),并解决直接将一个仓库拖拽到另一个仓库中导致的问题

git 将一个仓库拷贝到另一个仓库的文件夹下。默认git并不会处理&#xff0c;上传上去之后&#xff0c;只会创建一个文件夹&#xff0c;但是这个文件夹点不开。 在 git add . 的时候&#xff0c;会报出警告&#xff1a; 警告&#xff1a;正在添加嵌入式 git 仓库&#xff1a;cl…

微链接: 利用 MinIO 实现计算和存储的还原

Microblink 是一家专门从事图像检测的 AI 公司。他们从 BlinkID、BlinkID Verify 和 BlinkCard 等产品开始进入身份空间。最近&#xff0c;他们的图像检测能力催生了可以处理其他类型图像的产品。例如&#xff0c;可以对收据执行产品检测&#xff0c;从而使用收据上的产品描述来…

Update Azure OpenAI npm Package to 2023-12-01-preview Version

题意&#xff1a;将 Azure OpenAI npm 包更新到 2023-12-01-preview 版本 问题背景&#xff1a; I am currently using the azure-openai npm package in my project with version 2023-03-15-preview. As per the latest updates, version 2023-12-01-preview is available a…

【论文分享】MyTEE: Own the Trusted Execution Environment on Embedded Devices 23‘NDSS

目录 AbstractINTRODUCTIONBACKGROUNDARMv8 ArchitectureSecurity statesTrustZone extensionsVirtualization Communication with Peripherals MOTIVATIONATTACK MODEL AND ASSUMPTIONSYSTEM DESIGNOverviewExecution Environments IsolationDMA FilterExternal DMA controlle…

负载均衡--资源申请说明(三)

1.负载方式&#xff1a;分为四层负载和七层负载 2.负载协议&#xff1a;四层负载为TCP和UDP&#xff0c;七层负载为HTTP和HTTPS 4.负载端口&#xff1a;填写虚地址的端口&#xff08;一般与后端服务端口保持一致&#xff09; 5.真实服务IP&#xff1a;指被负载的后台真实服务…

零售自动化新趋势:AI 智能名片与 S2B2C 商城系统助力零售业变革

摘要&#xff1a;本文深入探讨零售业所面临的多重压力&#xff0c;分析客户期望不断提高的现状&#xff0c;强调零售商追求自动化的必要性。引入零售自动化的概念&#xff0c;阐述其通过技术实现零售体验自动化或半自动化&#xff0c;提供线上线下无缝连接、快速和个性化体验的…

MATLAB实现跳频多频移键控通信系统仿真

1. 简介 在现代无线通信系统中&#xff0c;跳频技术和多频移键控&#xff08;MFSK&#xff09;调制被广泛应用于抗干扰和提高通信系统性能。本文将通过 MATLAB 仿真分析跳频 MFSK 通信系统的性能&#xff0c;特别是在不同信道干扰条件下的误码率&#xff08;BER&#xff09;表…

ultralytics框架实现ByteTrack目标追踪算法

在ultralytics框架中&#xff0c;提供了两种用于目标追踪的算法&#xff0c;分别是ByteTrack算法与Botsort算法&#xff0c;这两种算法都是在Sort算法的基础上改进的&#xff0c;今天&#xff0c;我们学习一下ByteTrack算法。 存在问题 首先&#xff0c;我们看下ByteTrack所解…

《数字信号处理》学习04-离散时间系统中的线性时不变系统

目录 一&#xff0c;系统及离散时间系统 二&#xff0c;离散时间系统中的线性时不变系统 1&#xff0c;线性系统 1) 可加性 2) 比例性(齐次性) 3&#xff09;叠加原理(叠加性质) 2&#xff0c;时不变系统(移不变系统) 通过前几篇文章的学习&#xff0c;此时我对序列的相关概…

毒枸杞事件启示录:EasyCVR视频AI智能监管方案如何重塑食品卫生安全防线

一、方案背景 近年来&#xff0c;食品安全问题频发&#xff0c;引发了社会各界的广泛关注。其中&#xff0c;毒枸杞事件尤为引人关注。新闻报道&#xff0c;在青海格尔木、甘肃靖远等地&#xff0c;部分商户为了提升枸杞的品相&#xff0c;违规使用焦亚硫酸钠和工业硫磺进行“…