Redis - 5 ( 18000 字 Redis 入门级教程 )
一: 补充知识
1.1 渐进式遍历
Redis 使用 scan 命令以渐进式方式遍历键,避免了直接使用 keys 命令可能引发的阻塞问题。scan 的时间复杂度为 O(1),但需要多次执行才能完成对所有键的遍历,整个过程分步进行,有效减少阻塞风险。首次执行时,scan 从游标 0 开始,当返回的下次游标为 0 时,遍历结束。每次执行 scan 会返回下次游标(cursor)和本次获取的键。不过,虽然 scan 的渐进式遍历解决了阻塞问题,但在遍历期间如果键发生变化(如增加、修改或删除),可能会导致键的重复遍历或遗漏,这一点需要在实际开发中加以考虑。
SCAN cursor [MATCH pattern] [COUNT count] [TYPE type]
redis 127.0.0.1:6379> scan 0
1) "17"
2) 1) "key:12"
2) "key:8"
3) "key:4"
4) "key:14"
5) "key:16"
6) "key:17"
7) "key:15"
8) "key:10"
9) "key:3"
10) "key:7"
11) "key:1"redis 127.0.0.1:6379> scan 17
1) "0"
2) 1) "key:5"
2) "key:18"
3) "key:0"
4) "key:2"
5) "key:19"
6) "key:13"
7) "key:6"
8) "key:9"
9) "key:11"
1.2 数据库管理
Redis 提供了几个针对数据库操作的常用命令,包括 dbsize、select、flushdb 和 flushall。接下来将通过具体示例介绍这些命令的常见用法。
1.2.1 切换数据库
许多关系型数据库(如 MySQL)支持在一个实例下存在多个数据库,并通过字符区分数据库名称,而 Redis 则使用数字来区分数据库。Redis 默认配置中提供 16 个数据库,默认处于 0 号数据库。通过 select 0 可以切换到第一个数据库,而 select 15 则切换到最后一个数据库。不同数据库之间的数据是完全独立的,各自保存各自的键值对,互不冲突。
虽然 Redis 支持多数据库,但随着版本升级,官方并不推荐使用这一特性。如果需要完全隔离的两套键值对,更好的方案是维护多个 Redis 实例,而不是在一个实例中使用多个数据库。这是因为 Redis 对多数据库的支持较为有限,同时无论是否使用多数据库,Redis 都采用单线程模型,多个数据库之间仍需排队等待命令执行。此外,使用多数据库会增加开发、调试和运维的复杂性。因此,在实际应用中,始终使用默认的 0 号数据库通常是更好的选择。
select dbIndex
1.2.2 清除数据库
flushdb 和 flushall 命令用于清除数据库,区别在于 flushdb 仅清除当前数据库的数据,而 flushall 会清除所有数据库的数据。在实际应用中,除非必要,永远不要在生产环境中执行清除数据的操作,否则可能会导致严重后果。
二: 使用 Java 操作 redis 的客户端
在 Java 中操作 Redis 的客户端有很多,其中最常用且知名的是 Jedis。首先创建一个 Maven 项目,并将 Jedis 的依赖添加到 pom.xml 文件中:
<dependency><groupId>redis.clients</groupId><artifactId>jedis</artifactId><version>4.3.2</version>
</dependency>
2.1 配置端口转发
如果 Redis 服务器安装在云服务器上,而代码运行在本地主机上,为了让本地主机访问 Redis,需要在云服务器后台(如防火墙或安全组)开放 Redis 的端口到公网。但这种操作非常危险,因为可能会被黑客利用 Redis 端口入侵。为了解决这一问题,可以使用端口转发的方式,将服务器的 Redis 端口映射到本地。在 Xshell 中,可以通过相应配置实现这一功能,从而在保证安全的前提下访问远程 Redis 服务。
步骤 | 操作说明 |
---|---|
1 | 右键选择云服务器的会话,点击“属性”。 |
2 | 进入“隧道”选项卡,配置转发规则。 |
接着使用该会话连接服务器,此时访问本地的 8888 端口就相当于访问服务器的 6379 端口。需要注意的是,Xshell 必须与服务器保持连接状态,映射才能生效。
2.2 连接 Redis Server
步骤 | 操作说明 |
---|---|
1 | 使用 JedisPool 描述 Redis 服务器的位置,并通过 URL 表示。 |
2 | 使用 getResource 方法与 Redis 服务器建立连接。 |
3 | 连接使用完毕后需要调用 close 方法关闭,或者直接使用 try 自动关闭连接。 |
4 | 通过 ping 方法检测连接是否正确建立。 |
JedisPool jedisPool = new JedisPool("tcp://127.0.0.1:8888");try (Jedis jedis = jedisPool.getResource()) {String ping = jedis.ping(); // 这个方法会返回 PONGSystem.out.println(ping); // 输出 PONG
}
这个报错不影响使用,忽略即可,下面讲解一下 Java 操作 redis 的客户端的基本操作
2.3 set 和 get
当 key 不存在时,使用 get 方法获取的 value 将为 null。
public static void main(String[] args) {JedisPool jedisPool = new JedisPool("tcp://127.0.0.1:8888");try (Jedis jedis = jedisPool.getResource()) {testGetSet(jedis);jedis.flushDB(); // 清空数据库}
}private static void testGetSet(Jedis jedis) {jedis.set("key1", "value1"); // 设置 key1 的值为 value1jedis.set("key2", "value2"); // 设置 key2 的值为 value2String value1 = jedis.get("key1");System.out.println(value1); // 输出: value1String value2 = jedis.get("key2");System.out.println(value2); // 输出: value2String valueNull = jedis.get("noSuchKey");System.out.println(valueNull); // 输出: null
}
2.4 exists 和 del
del 方法可以删除多个 key,以可变参数列表的形式传入,返回值表示实际删除的 key 的数量。
private static void testExistsAndDel(Jedis jedis) {jedis.set("key1", "value"); // 设置 key1 的值为 valuejedis.set("key2", "value"); // 设置 key2 的值为 valuejedis.set("key3", "value"); // 设置 key3 的值为 valueboolean ret = jedis.exists("key1");System.out.println(ret); // 输出: true (key1 存在)long n = jedis.del("key1");System.out.println(n); // 输出: 1 (成功删除了 1 个 key)ret = jedis.exists("key1");System.out.println(ret); // 输出: false (key1 已被删除)n = jedis.del("key2", "key3");System.out.println(n); // 输出: 2 (成功删除了 2 个 key)ret = jedis.exists("key2");System.out.println(ret); // 输出: false (key2 已被删除)
}
2.5 keys
private static void testKeys(Jedis jedis) {jedis.set("key1", "value1"); // 设置 key1 的值为 value1jedis.set("key2", "value2"); // 设置 key2 的值为 value2jedis.set("key3", "value3"); // 设置 key3 的值为 value3jedis.set("myKey", "value4"); // 设置 myKey 的值为 value4Set<String> keys = jedis.keys("*");System.out.println(keys); // 输出: [key1, key2, key3, myKey] (匹配所有键)keys = jedis.keys("key?");System.out.println(keys); // 输出: [key1, key2, key3] (匹配 key 后面跟一个字符的键)
}
2.6 expire 和 ttl
使用 setex 方法可以在设置 key 的同时指定其过期时间,过期时间的单位为秒。
private static void testExpireAndTTL(Jedis jedis) {jedis.setex("key", 60, "value"); // 设置 key 的值为 value,并指定过期时间为 60 秒long ttl = jedis.ttl("key");System.out.println(ttl); // 输出: 剩余的过期时间,单位为秒(如:60,表示还有 60 秒过期)
}
2.7 type
private static void testType(Jedis jedis) {jedis.set("key1", "value"); // 设置 key1 的值为 valueSystem.out.println(jedis.type("key1")); // 输出: string (key1 是字符串类型)jedis.lpush("key2", "a", "b", "c"); // 向 key2 的列表中依次从左插入 a, b, cSystem.out.println(jedis.type("key2")); // 输出: list (key2 是列表类型)jedis.hset("key3", "name", "zhangsan"); // 设置 key3 的哈希字段 name 为 zhangsanSystem.out.println(jedis.type("key3")); // 输出: hash (key3 是哈希类型)jedis.sadd("key4", "111", "222", "333"); // 向 key4 的集合中添加 111, 222, 333System.out.println(jedis.type("key4")); // 输出: set (key4 是集合类型)jedis.zadd("key5", 1, "aaa"); // 向 key5 的有序集合中添加元素 aaa,分值为 1System.out.println(jedis.type("key5")); // 输出: zset (key5 是有序集合类型)
}
2.8 mget 和 mset
private static void testMSetAndMGet(Jedis jedis) {jedis.mset("key1", "value1", "key2", "value2", "key3", "value3"); // 批量设置 key-value 键值对List<String> values = jedis.mget("key1", "key2", "key3"); // 批量获取 key1, key2, key3 的值System.out.println(values); // 输出: [value1, value2, value3] (返回对应键的值列表)
}
2.9 append
private static void testAppend(Jedis jedis) {jedis.append("key", "aaa"); // 向 key 的值后追加字符串 aaaString value = jedis.get("key");System.out.println(value); // 输出: aaa (key 的当前值)jedis.append("key", "bbb"); // 向 key 的值后追加字符串 bbbvalue = jedis.get("key");System.out.println(value); // 输出: aaabbb (key 的当前值)
}
2.10 getrange 和 setrange
需要注意 getrange 方法的区间是闭区间
private static void testGetRangeAndSetRange(Jedis jedis) {jedis.set("key", "abcdefg"); // 设置 key 的值为 "abcdefg"String value = jedis.getrange("key", 1, 4); // 获取 key 的值中从索引 1 到 4 的子串(闭区间)System.out.println(value); // 输出: bcdejedis.setrange("key", 0, "xyz"); // 从索引 0 开始,用 "xyz" 替换 key 的值部分内容value = jedis.get("key");System.out.println(value); // 输出: xyzdefg
}
2.11 setnx
private static void testSetnx(Jedis jedis) {long n = jedis.setnx("key", "value"); // 如果 key 不存在,则设置 key 的值为 "value"System.out.println(n); // 输出: 1 (key 不存在,设置成功)String value = jedis.get("key");System.out.println(value); // 输出: value (当前 key 的值)n = jedis.setnx("key", "value2"); // 如果 key 已存在,不会修改值System.out.println(n); // 输出: 0 (key 已存在,设置失败)value = jedis.get("key");System.out.println(value); // 输出: value (key 的值仍为原来的值)
}
2.12 psetex
通过 pttl 获取的结果不一定正好是 1000 毫秒,因为 pttl 本身存在时间开销。
private static void testPsetexAndPttl(Jedis jedis) {jedis.psetex("key", 1000, "value"); // 设置 key 的值为 "value",并指定过期时间为 1000 毫秒long ttl = jedis.pttl("key"); // 获取 key 剩余的过期时间(单位:毫秒)System.out.println(ttl); // 输出: 一个接近 1000 的数值(因为存在时间开销,不一定正好是 1000)
}
2.13 incr 和 decr
private static void testIncrAndDecr(Jedis jedis) {jedis.set("key", "0"); // 设置 key 的初始值为 "0"jedis.incr("key"); // 将 key 的值自增 1System.out.println(jedis.get("key")); // 输出: 1 (当前 key 的值)jedis.decr("key"); // 将 key 的值自减 1System.out.println(jedis.get("key")); // 输出: 0 (当前 key 的值)
}
2.14 incrby 和 decrby
private static void testIncrByAndDecrBy(Jedis jedis) {jedis.set("key", "0"); // 设置 key 的初始值为 "0"jedis.incrBy("key", 10); // 将 key 的值增加 10System.out.println(jedis.get("key")); // 输出: 10 (当前 key 的值)jedis.decrBy("key", 5); // 将 key 的值减少 5System.out.println(jedis.get("key")); // 输出: 5 (当前 key 的值)
}
2.15 lpush 和 lpop
private static void testLpushAndLpop(Jedis jedis) {long n = jedis.lpush("key", "1", "2", "3", "4"); // 从左侧依次插入 4, 3, 2, 1System.out.println(n); // 输出: 4 (列表中元素的总数量)String value = jedis.lpop("key"); // 从左侧弹出第一个元素System.out.println(value); // 输出: 4 (第一个弹出的元素)value = jedis.lpop("key"); // 弹出下一个元素System.out.println(value); // 输出: 3value = jedis.lpop("key"); // 弹出下一个元素System.out.println(value); // 输出: 2value = jedis.lpop("key"); // 弹出下一个元素System.out.println(value); // 输出: 1value = jedis.lpop("key"); // 列表为空时,再次弹出System.out.println(value); // 输出: null (列表为空)
}
2.16 rpush 和 rpop
private static void testRpushAndRpop(Jedis jedis) {long n = jedis.rpush("key", "1", "2", "3", "4"); // 从右侧依次插入 1, 2, 3, 4System.out.println(n); // 输出: 4 (列表中元素的总数量)String value = jedis.rpop("key"); // 从右侧弹出第一个元素System.out.println(value); // 输出: 4 (第一个弹出的元素)value = jedis.rpop("key"); // 弹出下一个元素System.out.println(value); // 输出: 3value = jedis.rpop("key"); // 弹出下一个元素System.out.println(value); // 输出: 2value = jedis.rpop("key"); // 弹出下一个元素System.out.println(value); // 输出: 1value = jedis.rpop("key"); // 列表为空时,再次弹出System.out.println(value); // 输出: null (列表为空)
}
2.17 lrange
private static void testLrange(Jedis jedis) {jedis.rpush("key", "1", "2", "3", "4"); // 从右侧依次插入 1, 2, 3, 4List<String> values = jedis.lrange("key", 1, 3); // 获取列表中从索引 1 到 3 的元素(闭区间)System.out.println(values); // 输出: [2, 3, 4] (索引范围内的元素)
}
2.18 blpop
blpop 返回的结果是一个二元组形式的 List,其中索引 [0] 表示 key,索引 [1] 表示 value。将超时时间设置为 0 表示阻塞等待,直到有数据可弹出。在执行 blpop 的同时,可以通过另一个 redis-cli 客户端插入数据,便可实时查看 blpop 的返回结果。
private static void testBLpop(Jedis jedis) {while (true) {List<String> values = jedis.blpop(0, "key"); // 阻塞等待,从列表 key 中弹出第一个元素System.out.println(values); // 输出: [key, value] (返回包含 key 和弹出的值的二元组)}
}
2.19 brpop
private static void testBRpop(Jedis jedis) {System.out.println("开始调用 brpop"); // 提示 brpop 调用开始while (true) {List<String> values = jedis.brpop(0, "key"); // 阻塞等待,从列表 key 中从右侧弹出元素System.out.println(values); // 输出: [key, value] (返回包含 key 和弹出的值的二元组)}
}
2.20 lindex
private static void testLindex(Jedis jedis) {jedis.rpush("key", "1", "2", "3", "4"); // 从右侧依次插入 1, 2, 3, 4String value = jedis.lindex("key", 2); // 获取列表 key 中索引为 2 的元素System.out.println(value); // 输出: 3 (索引 2 对应的元素)
}
2.21linsert
linsert 方法通过指定插入位置标识 ListPosition.BEFORE 或 ListPosition.AFTER,决定新元素是插入到目标元素之前还是之后。
private static void testLinsert(Jedis jedis) {jedis.rpush("key", "a", "b", "c", "d"); // 从右侧依次插入 a, b, c, djedis.linsert("key", ListPosition.BEFORE, "c", "100"); // 在元素 c 之前插入 100List<String> values = jedis.lrange("key", 0, -1); // 获取列表 key 中所有元素System.out.println(values); // 输出: [a, b, 100, c, d] (列表中插入后的结果)
}
2.22 llen
private static void testLlen(Jedis jedis) {jedis.rpush("key", "a", "b", "c", "d"); // 从右侧依次插入 a, b, c, dlong n = jedis.llen("key"); // 获取列表 key 的长度System.out.println(n); // 输出: 4 (列表中元素的总数量)
}
2.23 hset 和 hget
private static void testHsetAndHget(Jedis jedis) {jedis.hset("key", "name", "zhangsan"); // 设置哈希表 key 中字段 name 的值为 zhangsanjedis.hset("key", "age", "20"); // 设置哈希表 key 中字段 age 的值为 20String name = jedis.hget("key", "name"); // 获取哈希表 key 中字段 name 的值System.out.println(name); // 输出: zhangsanString age = jedis.hget("key", "age"); // 获取哈希表 key 中字段 age 的值System.out.println(age); // 输出: 20
}
2.24 hexists 和 hdel
private static void testHexistsAndHdel(Jedis jedis) {jedis.hset("key", "name", "zhangsan"); // 设置哈希表 key 中字段 name 的值为 zhangsanboolean ok = jedis.hexists("key", "name"); // 检查哈希表 key 中是否存在字段 nameSystem.out.println(ok); // 输出: true (字段 name 存在)jedis.hdel("key", "name"); // 删除哈希表 key 中字段 nameok = jedis.hexists("key", "name"); // 再次检查哈希表 key 中是否存在字段 nameSystem.out.println(ok); // 输出: false (字段 name 已被删除)
}
2.25 hkeys 和 hvals
private static void testHkeysAndHvalues(Jedis jedis) {jedis.hset("key", "name", "zhangsan"); // 设置哈希表 key 中字段 name 的值为 zhangsanjedis.hset("key", "age", "20"); // 设置哈希表 key 中字段 age 的值为 20Set<String> keys = jedis.hkeys("key"); // 获取哈希表 key 中所有字段名System.out.println(keys); // 输出: [name, age] (哈希表中的字段名集合)List<String> values = jedis.hvals("key"); // 获取哈希表 key 中所有字段的值System.out.println(values); // 输出: [zhangsan, 20] (哈希表中的字段值集合)
}
2.26 hmget
private static void testHmget(Jedis jedis) {jedis.hset("key", "name", "zhangsan"); // 设置哈希表 key 中字段 name 的值为 zhangsanjedis.hset("key", "age", "20"); // 设置哈希表 key 中字段 age 的值为 20List<String> values = jedis.hmget("key", "name", "age"); // 批量获取哈希表 key 中字段 name 和 age 的值System.out.println(values); // 输出: [zhangsan, 20] (字段对应的值集合)
}
2.27 hlen
private static void testHlen(Jedis jedis) {jedis.hset("key", "name", "zhangsan"); // 设置哈希表 key 中字段 name 的值为 zhangsanjedis.hset("key", "age", "20"); // 设置哈希表 key 中字段 age 的值为 20long n = jedis.hlen("key"); // 获取哈希表 key 中字段的数量System.out.println(n); // 输出: 2 (字段的总数量)
}
2.28 hincrby 和 hincrbyfloat
private static void testHIncrByAndIncrByFloat(Jedis jedis) {jedis.hset("key", "age", "20"); // 设置哈希表 key 中字段 age 的值为 20long n = jedis.hincrBy("key", "age", 10); // 将哈希表 key 中字段 age 的值增加 10System.out.println(n); // 输出: 30 (增加后的值)String value = jedis.hget("key", "age"); // 获取哈希表 key 中字段 age 的值System.out.println(value); // 输出: 30double dn = jedis.hincrByFloat("key", "age", 0.5); // 将哈希表 key 中字段 age 的值增加 0.5System.out.println(dn); // 输出: 30.5 (增加后的值)value = jedis.hget("key", "age"); // 再次获取哈希表 key 中字段 age 的值System.out.println(value); // 输出: 30.5
}
2.29 sadd 和 smembers
private static void testSaddAndSmembers(Jedis jedis) {jedis.sadd("key", "aaa", "bbb", "ccc"); // 向集合 key 中添加元素 aaa, bbb, cccSet<String> members = jedis.smembers("key"); // 获取集合 key 中的所有成员System.out.println(members); // 输出: [aaa, bbb, ccc] (集合中的所有成员,顺序可能无序)
}
2.30 srem 和 sismember
private static void testSremAndSismember(Jedis jedis) {jedis.sadd("key", "aaa", "bbb", "ccc"); // 向集合 key 中添加元素 aaa, bbb, cccboolean ok = jedis.sismember("key", "aaa"); // 检查集合 key 中是否包含元素 aaaSystem.out.println(ok); // 输出: true (aaa 存在于集合中)long n = jedis.srem("key", "aaa", "bbb"); // 从集合 key 中删除元素 aaa 和 bbbSystem.out.println(n); // 输出: 2 (成功删除的元素个数)ok = jedis.sismember("key", "aaa"); // 再次检查集合 key 中是否包含元素 aaaSystem.out.println(ok); // 输出: false (aaa 已被删除)
}
2.31 scard
private static void testScard(Jedis jedis) {jedis.sadd("key", "aaa", "bbb", "ccc"); // 向集合 key 中添加元素 aaa, bbb, ccclong n = jedis.scard("key"); // 获取集合 key 中的元素数量System.out.println(n); // 输出: 3 (集合中元素的总数量)
}
2.32 sinter
private static void testSinter(Jedis jedis) {jedis.sadd("key1", "aaa", "bbb", "ccc"); // 向集合 key1 中添加元素 aaa, bbb, cccjedis.sadd("key2", "aaa", "bbb", "ddd"); // 向集合 key2 中添加元素 aaa, bbb, dddSet<String> results = jedis.sinter("key1", "key2"); // 获取 key1 和 key2 的交集System.out.println(results); // 输出: [aaa, bbb] (两个集合的交集元素)
}
2.33 sunion
private static void testSunion(Jedis jedis) {jedis.sadd("key1", "aaa", "bbb", "ccc"); // 向集合 key1 中添加元素 aaa, bbb, cccjedis.sadd("key2", "aaa", "bbb", "ddd"); // 向集合 key2 中添加元素 aaa, bbb, dddSet<String> results = jedis.sunion("key1", "key2"); // 获取 key1 和 key2 的并集System.out.println(results); // 输出: [aaa, bbb, ccc, ddd] (两个集合的并集元素)
}
2.34 sdiff
private static void testSdiff(Jedis jedis) {jedis.sadd("key1", "aaa", "bbb", "ccc"); // 向集合 key1 中添加元素 aaa, bbb, cccjedis.sadd("key2", "aaa", "bbb", "ddd"); // 向集合 key2 中添加元素 aaa, bbb, dddSet<String> results = jedis.sdiff("key1", "key2"); // 获取 key1 和 key2 的差集 (key1 中有但 key2 中没有的元素)System.out.println(results); // 输出: [ccc] (key1 中独有的元素)
}
2.35 zadd 和 zrange
private static void testZaddAndZrange(Jedis jedis) {jedis.zadd("key", 100, "吕布"); // 向有序集合 key 中添加成员 吕布,分值为 100jedis.zadd("key", 98, "赵云"); // 向有序集合 key 中添加成员 赵云,分值为 98jedis.zadd("key", 95, "典韦"); // 向有序集合 key 中添加成员 典韦,分值为 95jedis.zadd("key", 92, "关羽"); // 向有序集合 key 中添加成员 关羽,分值为 92jedis.zadd("key", 70, "刘备"); // 向有序集合 key 中添加成员 刘备,分值为 70List<String> members = jedis.zrange("key", 0, 4); // 获取 key 中索引范围 0 到 4 的成员System.out.println(members); // 输出: [刘备, 关羽, 典韦, 赵云, 吕布] (按照分值从低到高排序的成员)List<Tuple> membersWithScore = jedis.zrangeWithScores("key", 0, 4); // 获取 key 中索引范围 0 到 4 的成员及其分值System.out.println(membersWithScore); // 输出: [刘备=70.0, 关羽=92.0, 典韦=95.0, 赵云=98.0, 吕布=100.0]
}
2.36 zrem 和 zcard
private static void testZremAndZcard(Jedis jedis) {jedis.zadd("key", 100, "吕布"); // 向有序集合 key 中添加成员 吕布,分值为 100jedis.zadd("key", 98, "赵云"); // 向有序集合 key 中添加成员 赵云,分值为 98jedis.zadd("key", 95, "典韦"); // 向有序集合 key 中添加成员 典韦,分值为 95jedis.zadd("key", 92, "关羽"); // 向有序集合 key 中添加成员 关羽,分值为 92jedis.zadd("key", 70, "刘备"); // 向有序集合 key 中添加成员 刘备,分值为 70long n = jedis.zcard("key"); // 获取有序集合 key 中的成员数量System.out.println(n); // 输出: 5 (集合中总的成员数量)n = jedis.zrem("key", "吕布", "赵云"); // 从有序集合 key 中移除成员 吕布 和 赵云System.out.println(n); // 输出: 2 (成功移除的成员数量)n = jedis.zcard("key"); // 再次获取有序集合 key 中的成员数量System.out.println(n); // 输出: 3 (移除成员后的总数量)
}
2.37 zcount
private static void testZcount(Jedis jedis) {jedis.zadd("key", 100, "吕布"); // 向有序集合 key 中添加成员 吕布,分值为 100jedis.zadd("key", 98, "赵云"); // 向有序集合 key 中添加成员 赵云,分值为 98jedis.zadd("key", 95, "典韦"); // 向有序集合 key 中添加成员 典韦,分值为 95jedis.zadd("key", 92, "关羽"); // 向有序集合 key 中添加成员 关羽,分值为 92jedis.zadd("key", 70, "刘备"); // 向有序集合 key 中添加成员 刘备,分值为 70long n = jedis.zcount("key", 92, 98); // 获取分值在 92 到 98 范围内的成员数量,闭区间System.out.println(n); // 输出: 3 (关羽、典韦、赵云 分值在该范围内)
}
2.38 zpopmax 和 zpopmin
private static void testZpopmaxAndZpopmin(Jedis jedis) {jedis.zadd("key", 100, "吕布"); // 向有序集合 key 中添加成员 吕布,分值为 100jedis.zadd("key", 98, "赵云"); // 向有序集合 key 中添加成员 赵云,分值为 98jedis.zadd("key", 95, "典韦"); // 向有序集合 key 中添加成员 典韦,分值为 95jedis.zadd("key", 92, "关羽"); // 向有序集合 key 中添加成员 关羽,分值为 92jedis.zadd("key", 70, "刘备"); // 向有序集合 key 中添加成员 刘备,分值为 70Tuple tuple = jedis.zpopmax("key"); // 弹出有序集合 key 中分值最高的成员System.out.println(tuple); // 输出: 吕布=100.0 (分值最高的成员及其分值)tuple = jedis.zpopmin("key"); // 弹出有序集合 key 中分值最低的成员System.out.println(tuple); // 输出: 刘备=70.0 (分值最低的成员及其分值)
}
2.39 zrank
private static void testZrank(Jedis jedis) {jedis.zadd("key", 100, "吕布"); // 向有序集合 key 中添加成员 吕布,分值为 100jedis.zadd("key", 98, "赵云"); // 向有序集合 key 中添加成员 赵云,分值为 98jedis.zadd("key", 95, "典韦"); // 向有序集合 key 中添加成员 典韦,分值为 95jedis.zadd("key", 92, "关羽"); // 向有序集合 key 中添加成员 关羽,分值为 92jedis.zadd("key", 70, "刘备"); // 向有序集合 key 中添加成员 刘备,分值为 70long n = jedis.zrank("key", "赵云"); // 获取成员 赵云 在有序集合 key 中的排名(从低到高)System.out.println(n); // 输出: 3 (赵云 的排名,从 0 开始)n = jedis.zrevrank("key", "赵云"); // 获取成员 赵云 在有序集合 key 中的排名(从高到低)System.out.println(n); // 输出: 1 (赵云 的排名,从 0 开始)
}
2.40 zscore
private static void testZscore(Jedis jedis) {jedis.zadd("key", 100, "吕布"); // 向有序集合 key 中添加成员 吕布,分值为 100jedis.zadd("key", 98, "赵云"); // 向有序集合 key 中添加成员 赵云,分值为 98jedis.zadd("key", 95, "典韦"); // 向有序集合 key 中添加成员 典韦,分值为 95jedis.zadd("key", 92, "关羽"); // 向有序集合 key 中添加成员 关羽,分值为 92jedis.zadd("key", 70, "刘备"); // 向有序集合 key 中添加成员 刘备,分值为 70double score = jedis.zscore("key", "赵云"); // 获取成员 赵云 在有序集合 key 中的分值System.out.println(score); // 输出: 98.0 (赵云 的分值)
}
2.41 zincrby
private static void testZincrby(Jedis jedis) {jedis.zadd("key", 100, "吕布"); // 向有序集合 key 中添加成员 吕布,分值为 100double n = jedis.zincrby("key", 10, "吕布"); // 增加成员 吕布 的分值 10System.out.println(n); // 输出: 110.0 (吕布 的新分值)n = jedis.zincrby("key", -20, "吕布"); // 减少成员 吕布 的分值 20System.out.println(n); // 输出: 90.0 (吕布 的新分值)
}
2.42 zinterstore
private static void testZinterstore(Jedis jedis) {jedis.zadd("key1", 100, "吕布"); // 向有序集合 key1 中添加成员 吕布,分值为 100jedis.zadd("key1", 98, "赵云"); // 向有序集合 key1 中添加成员 赵云,分值为 98jedis.zadd("key1", 95, "典韦"); // 向有序集合 key1 中添加成员 典韦,分值为 95jedis.zadd("key2", 100, "吕布"); // 向有序集合 key2 中添加成员 吕布,分值为 100jedis.zadd("key2", 98, "赵云"); // 向有序集合 key2 中添加成员 赵云,分值为 98jedis.zadd("key2", 92, "关羽"); // 向有序集合 key2 中添加成员 关羽,分值为 92long n = jedis.zinterstore("key3", "key1", "key2"); // 将 key1 和 key2 的交集存储到 key3 中System.out.println(n); // 输出: 2 (交集中成员的数量)List<Tuple> tuples = jedis.zrangeWithScores("key3", 0, -1); // 获取 key3 中的所有成员及其分值System.out.println(tuples); // 输出: [赵云=196.0, 吕布=200.0] (交集成员及其分值)
}
2.43 zunionstore
private static void testZunionstore(Jedis jedis) {jedis.zadd("key1", 100, "吕布"); // 向有序集合 key1 中添加成员 吕布,分值为 100jedis.zadd("key1", 98, "赵云"); // 向有序集合 key1 中添加成员 赵云,分值为 98jedis.zadd("key1", 95, "典韦"); // 向有序集合 key1 中添加成员 典韦,分值为 95jedis.zadd("key2", 100, "吕布"); // 向有序集合 key2 中添加成员 吕布,分值为 100jedis.zadd("key2", 98, "赵云"); // 向有序集合 key2 中添加成员 赵云,分值为 98jedis.zadd("key2", 92, "关羽"); // 向有序集合 key2 中添加成员 关羽,分值为 92long n = jedis.zunionstore("key3", "key1", "key2"); // 将 key1 和 key2 的并集存储到 key3 中System.out.println(n); // 输出: 4 (并集中成员的数量)List<Tuple> tuples = jedis.zrangeWithScores("key3", 0, -1); // 获取 key3 中的所有成员及其分值System.out.println(tuples); // 输出: [典韦=95.0, 关羽=92.0, 赵云=196.0, 吕布=200.0] (并集成员及其分值)
}
三: 访问集群
使用 JedisCluster 类可以访问 Redis 集群,替代单节点模式下使用的 Jedis 类。在创建 JedisCluster 实例时,需要将多个节点的地址配置进去。JedisCluster 提供的方法与 Jedis 基本一致,都与 Redis 命令一一对应,具体使用细节此处不再演示。
public static void main(String[] args) {// 创建一个存储 Redis 集群节点的集合Set<HostAndPort> nodes = new HashSet<>();nodes.add(new HostAndPort("172.30.0.101", 6379));nodes.add(new HostAndPort("172.30.0.102", 6379));nodes.add(new HostAndPort("172.30.0.103", 6379));nodes.add(new HostAndPort("172.30.0.104", 6379));nodes.add(new HostAndPort("172.30.0.105", 6379));nodes.add(new HostAndPort("172.30.0.106", 6379));nodes.add(new HostAndPort("172.30.0.107", 6379));nodes.add(new HostAndPort("172.30.0.108", 6379));nodes.add(new HostAndPort("172.30.0.109", 6379));// 创建 JedisCluster 实例并执行操作try (JedisCluster jedisCluster = new JedisCluster(nodes)) {jedisCluster.set("k1", "111"); // 设置键 k1 的值为 111String value = jedisCluster.get("k1"); // 获取键 k1 的值System.out.println(value); // 输出: 111}
}
注意,由于代码需要访问整个 Redis 集群,Windows 主机无法直接访问 Docker 容器的 IP 地址,因此无法在 Windows 上直接运行程序。需要将程序打包成 JAR 文件,上传到 Linux 环境中,并在 Linux 上运行程序以实现对 Redis 集群的访问。
java -jar [jar包名]