Redis的Java客户端主要有Jedis和Lettuce,本篇文章将会讲解使用Jedis操作Reids。
Jedis 是一个同步的 Redis Java 客户端。如果需要一个更高级的 Java 客户端,同时支持异步和响应式连接,请使用 Lettuce 。Github地址:https://github.com/redis/jedis 。Jedis版本和Redis版本的对应关系图如下所示:

这里我使用了6.2.1版本的redis,所以需要选择>=5.0版本的jedis,这里选择5.1.5版本(具体参照maven中央仓库中的版本号)。
一、Hello,World
先看Hello,World级别的案例:下面建立和Reids Server的连接,并执行set命令:
import lombok.extern.slf4j.Slf4j;
import org.junit.Test;
import redis.clients.jedis.UnifiedJedis;
/**
* @author kdyzm
* @date 2025/7/28
*/
@Slf4j
public class Jedis_demo {
@Test
public void hello() {
UnifiedJedis jedis = new UnifiedJedis("redis://192.168.203.130:6379");
jedis.set("result", "Hello,World");
String result = jedis.get("result");
log.info("get result={}", result);
jedis.close();
}
}
输出信息:
14:57:55.008 [main] INFO cn.kdyzm.jedis.demo.Jedis_demo - get result=Hello,World
可以看到使用jedis非常简单,它的命令和redis操作命令几乎一模一样。
二、连接到服务器
Redis部署有多种模式:单机版、主从复制模式、哨兵模式、集群模式,因此连接方式也有多种方式。
1、单机连接
很长时间以来,Jedis一直使用Jedis类作为客户端连接服务器,使用方法如下:
@Test
public void jedis_demo() {
Jedis jedis = new Jedis(new HostAndPort("192.168.203.130",6379));
jedis.set("result", "Hello,World");
String result = jedis.get("result");
log.info("get result={}", result);
jedis.close();
}
在Jedis4.0的时候引入了新客户端:UnifiedJedis,统一了同步(阻塞)和异步(非阻塞)操作的API,支持更灵活的连接管理(如单连接或多连接模式)。
@Test
public void hello() {
UnifiedJedis jedis = new UnifiedJedis("redis://192.168.203.130:6379");
jedis.set("result", "Hello,World");
String result = jedis.get("result");
log.info("get result={}", result);
jedis.close();
}
可以看到两者在使用上几乎没有区别,只是在初始化的时候有些不同。
当然就像数据库连接一样,我们不推荐直接使用jdbc创建Connection,而是通过Reids连接池来统一管理连接:
/**
* 经典的jedis连接池
*/
@Test
public void jedis_pool_test() {
JedisPool jedisPool = new JedisPool("192.168.203.130", 6379);
Jedis jedis = jedisPool.getResource();
jedis.set("result", "Hello,World");
String result = jedis.get("result");
log.info("get result={}", result);
jedis.close();
}
/**
* jedis4.0引入的新连接池
*/
@Test
public void jedis_pooled_test() {
JedisPooled jedisPool = new JedisPooled("192.168.203.130", 6379);
jedisPool.set("result", "Hello,World");
String result = jedisPool.get("result");
log.info("get result={}", result);
}
其中jedis4.0引入的JedisPooled连接池使用起来更简单一些,不需要手动释放资源,也不需要显式获取Reids客户端。
2、哨兵模式
jedis没有单独的为主从复制模式增加API,但是对哨兵模式的连接有专门的JedisSentinelPool
类:
@Test
public void jedis_pool_sentinel() {
JedisSentinelPool jedisSentinelPool = new JedisSentinelPool(
"mymaster",
new HashSet<>(Arrays.asList(
"192.168.203.130:26379",
"192.168.203.130:26380",
"192.168.203.130:26381"
))
);
try (Jedis resource = jedisSentinelPool.getResource()) {
resource.set("result", "Hello,World");
String result = resource.get("result");
log.info("get result={}", result);
}
}
运行结果如下:
15:54:37.102 [main] INFO redis.clients.jedis.JedisSentinelPool - Trying to find master from available Sentinels...
15:54:37.102 [main] DEBUG redis.clients.jedis.JedisSentinelPool - Connecting to Sentinel 192.168.203.130:26380
15:54:37.143 [main] DEBUG redis.clients.jedis.JedisSentinelPool - Found Redis master at 192.168.203.130:6379
15:54:37.143 [main] INFO redis.clients.jedis.JedisSentinelPool - Redis master running at 192.168.203.130:6379, starting Sentinel listeners...
15:54:37.147 [main] INFO redis.clients.jedis.JedisSentinelPool - Created JedisSentinelPool to master at 192.168.203.130:6379
15:54:37.151 [main] INFO cn.kdyzm.jedis.demo.Jedis_demo - get result=Hello,World
需要注意的是,sentinel集群中的配置一定不要写错了:
sentinel monitor mymaster 192.168.203.130 6379 2 #这里的ip地址一定不要写成了127.0.0.1
3、集群模式
jedis中使用JedisCluster 连接Redis集群模式:
@Test
public void jedis_cluster() {
JedisCluster jedisCluster = new JedisCluster(
new HostAndPort("192.168.203.130", 30001)
);
jedisCluster.set("result", "Hello,World");
String result = jedisCluster.get("result");
log.info("get result={}", result);
}
集群模式的连接也很简单,只需要传入集群任意节点的ip和端口号即可。
需要注意的是,在创建集群的时候ip地址一定不要写错了:
./redis-cli --cluster create 192.168.203.130:30001 192.168.203.130:30002 192.168.203.130:30003 192.168.203.130:30004 192.168.203.130:30005 192.168.203.130:30006 192.168.203.130:30007 192.168.203.130:30008 192.168.203.130:30009 192.168.203.130:30010 192.168.203.130:30011 --cluster-replicas 1
如果写错了的话会连接不上集群,修复集群中每个端口号会比较麻烦。我的修复方案比较暴力:
- 杀掉集群中全部reids进程,删除node配置文件,之后全部重新启动;
- 在集群中任意节点上使用
cluster meet
命令将节点一个一个加入集群 - 使用
./reids-cli --cluster check
命令检查集群状态,会发现此时cluster集群状态异常,不是所有的槽都分配了:使用./redis-cli --cluster fix
命令修复集群问题。
三、常用API
jedis的api设计和redis命令格式高度一致,所以基本上会使用redis命令,就知道怎么使用jedis了。
1、string类型操作
package cn.kdyzm.jedis.demo;
import org.junit.Test;
import redis.clients.jedis.JedisPooled;
import java.util.List;
import static org.junit.Assert.*;
public class StringDemo {
private static final String HOST = "192.168.203.130";
private static final int PORT = 6379;
private JedisPooled jedis = new JedisPooled(HOST, PORT);
@Test
public void testSetAndGetOperation() {
// 设置键值
String setResult = jedis.set("testKey", "testValue");
assertEquals("OK", setResult);
System.out.println("SET operation successful");
// 获取值
String value = jedis.get("testKey");
assertEquals("testValue", value);
System.out.println("GET operation successful - Value: " + value);
}
@Test
public void testSetexOperation() {
// 设置带过期时间的键值
String setexResult = jedis.setex("tempKey", 10, "tempValue");
assertEquals("OK", setexResult);
System.out.println("SETEX operation successful");
// 验证TTL
long ttl = jedis.ttl("tempKey");
assertTrue(ttl > 0 && ttl <= 10);
System.out.println("TTL verification successful - Remaining: " + ttl + " seconds");
}
@Test
public void testIncrAndDecrOperations() {
// 重置计数器
jedis.set("counter", "0");
// 测试递增
long incrResult = jedis.incr("counter");
assertEquals(1, incrResult);
System.out.println("INCR operation successful - New value: " + incrResult);
// 测试递减
long decrResult = jedis.decr("counter");
assertEquals(0, decrResult);
System.out.println("DECR operation successful - New value: " + decrResult);
}
@Test
public void testAppendOperation() {
// 设置初始值
jedis.set("appendKey", "initial");
// 测试追加
long appendLength = jedis.append("appendKey", "_appended");
assertTrue(appendLength > 7); // "initial".length() == 7
System.out.println("APPEND operation successful - New length: " + appendLength);
// 验证追加结果
String appendedValue = jedis.get("appendKey");
assertEquals("initial_appended", appendedValue);
System.out.println("Appended value verification successful: " + appendedValue);
}
@Test
public void testStrlenOperation() {
// 设置测试值
jedis.set("lengthKey", "123456789");
// 测试字符串长度
long length = jedis.strlen("lengthKey");
assertEquals(9, length);
System.out.println("STRLEN operation successful - Length: " + length);
}
@Test
public void testMsetAndMgetOperations() {
// 测试批量设置
String msetResult = jedis.mset("multi1", "value1", "multi2", "value2");
assertEquals("OK", msetResult);
System.out.println("MSET operation successful");
// 测试批量获取
List<String> values = jedis.mget("multi1", "multi2");
assertNotNull(values);
assertEquals(2, values.size());
assertEquals("value1", values.get(0));
assertEquals("value2", values.get(1));
System.out.println("MGET operation successful - Values: " + values);
}
}
2、hash类型操作
package cn.kdyzm.jedis.demo;
import org.junit.After;
import org.junit.Test;
import redis.clients.jedis.JedisPooled;
import java.util.Map;
import java.util.Set;
import java.util.List;
import static org.junit.Assert.*;
public class RedisHashOperationsTest {
private static final String HOST = "192.168.203.130";
private static final int PORT = 6379;
private JedisPooled jedis = new JedisPooled(HOST, PORT);
private static final String HASH_KEY = "user:1";
@Test
public void testHsetAndHget() {
// 设置字段值
long hsetResult1 = jedis.hset(HASH_KEY, "name", "Alice");
long hsetResult2 = jedis.hset(HASH_KEY, "age", "30");
assertTrue(hsetResult1 >= 0);
assertTrue(hsetResult2 >= 0);
System.out.println("HSET operations successful");
// 获取字段值
String name = jedis.hget(HASH_KEY, "name");
assertEquals("Alice", name);
System.out.println("HGET operation successful - Name: " + name);
}
@Test
public void testHgetAll() {
// 准备测试数据
jedis.hset(HASH_KEY, "name", "Alice");
jedis.hset(HASH_KEY, "age", "30");
// 获取所有字段和值
Map<String, String> user = jedis.hgetAll(HASH_KEY);
assertNotNull(user);
assertEquals(2, user.size());
assertEquals("Alice", user.get("name"));
assertEquals("30", user.get("age"));
System.out.println("HGETALL operation successful - User data: " + user);
}
@Test
public void testHdel() {
// 准备测试数据
jedis.hset(HASH_KEY, "age", "30");
// 删除字段
long deleted = jedis.hdel(HASH_KEY, "age");
assertEquals(1, deleted);
System.out.println("HDEL operation successful - Fields deleted: " + deleted);
// 验证字段已删除
boolean exists = jedis.hexists(HASH_KEY, "age");
assertFalse(exists);
System.out.println("Field deletion verification successful");
}
@Test
public void testHexists() {
// 准备测试数据
jedis.hset(HASH_KEY, "name", "Alice");
// 判断字段是否存在
boolean exists = jedis.hexists(HASH_KEY, "name");
assertTrue(exists);
System.out.println("HEXISTS operation successful - Field exists: " + exists);
// 验证不存在的字段
boolean notExists = jedis.hexists(HASH_KEY, "nonexistent");
assertFalse(notExists);
System.out.println("HEXISTS operation successful - Nonexistent field check passed");
}
@Test
public void testHkeys() {
// 准备测试数据
jedis.hset(HASH_KEY, "name", "Alice");
jedis.hset(HASH_KEY, "age", "30");
jedis.hset(HASH_KEY, "email", "alice@example.com");
// 获取所有字段名
Set<String> fields = jedis.hkeys(HASH_KEY);
assertNotNull(fields);
assertEquals(3, fields.size());
assertTrue(fields.contains("name"));
assertTrue(fields.contains("age"));
assertTrue(fields.contains("email"));
System.out.println("HKEYS operation successful - Fields: " + fields);
}
@Test
public void testHvals() {
// 准备测试数据
jedis.hset(HASH_KEY, "name", "Alice");
jedis.hset(HASH_KEY, "age", "30");
// 获取所有值
List<String> vals = jedis.hvals(HASH_KEY);
assertNotNull(vals);
assertEquals(2, vals.size());
assertTrue(vals.contains("Alice"));
assertTrue(vals.contains("30"));
System.out.println("HVALS operation successful - Values: " + vals);
}
@Test
public void testHincrBy() {
// 准备测试数据
jedis.hset(HASH_KEY, "age", "30");
// 递增字段值
long newAge = jedis.hincrBy(HASH_KEY, "age", 1);
assertEquals(31, newAge);
System.out.println("HINCRBY operation successful - New age: " + newAge);
// 验证新值
String updatedAge = jedis.hget(HASH_KEY, "age");
assertEquals("31", updatedAge);
System.out.println("Incremented value verification successful");
}
@After
public void cleanup() {
// 清理测试数据
jedis.del(HASH_KEY);
System.out.println("Test data cleaned up");
}
}
3、list类型操作
package cn.kdyzm.jedis.demo;
import org.junit.After;
import org.junit.Test;
import redis.clients.jedis.JedisPooled;
import java.util.List;
import static org.junit.Assert.*;
public class RedisListOperationsTest {
private static final String HOST = "192.168.203.130";
private static final int PORT = 6379;
private JedisPooled jedis = new JedisPooled(HOST, PORT);
private static final String LIST_KEY = "testList";
@Test
public void testLpushAndRpush() {
// 从左边插入元素
long lpushResult = jedis.lpush(LIST_KEY, "item1", "item2");
assertEquals(2, lpushResult);
System.out.println("LPUSH operation successful - Items added: " + lpushResult);
// 从右边插入元素
long rpushResult = jedis.rpush(LIST_KEY, "item3");
assertEquals(3, rpushResult);
System.out.println("RPUSH operation successful - Items count: " + rpushResult);
}
@Test
public void testLlen() {
// 准备测试数据
jedis.lpush(LIST_KEY, "item1", "item2", "item3");
// 获取列表长度
long length = jedis.llen(LIST_KEY);
assertEquals(3, length);
System.out.println("LLEN operation successful - List length: " + length);
}
@Test
public void testLrange() {
// 准备测试数据
jedis.rpush(LIST_KEY, "item1", "item2", "item3");
// 获取列表元素
List<String> items = jedis.lrange(LIST_KEY, 0, -1);
assertNotNull(items);
assertEquals(3, items.size());
assertEquals("item1", items.get(0));
assertEquals("item2", items.get(1));
assertEquals("item3", items.get(2));
System.out.println("LRANGE operation successful - All items: " + items);
}
@Test
public void testLpopAndRpop() {
// 准备测试数据
jedis.rpush(LIST_KEY, "item1", "item2", "item3");
// 弹出左边元素
String leftItem = jedis.lpop(LIST_KEY);
assertEquals("item1", leftItem);
System.out.println("LPOP operation successful - Popped item: " + leftItem);
// 弹出右边元素
String rightItem = jedis.rpop(LIST_KEY);
assertEquals("item3", rightItem);
System.out.println("RPOP operation successful - Popped item: " + rightItem);
// 验证剩余元素
long remaining = jedis.llen(LIST_KEY);
assertEquals(1, remaining);
System.out.println("Remaining items count: " + remaining);
}
@Test
public void testLindex() {
// 准备测试数据
jedis.rpush(LIST_KEY, "item1", "item2", "item3");
// 根据索引获取元素
String item = jedis.lindex(LIST_KEY, 1);
assertEquals("item2", item);
System.out.println("LINDEX operation successful - Item at index 1: " + item);
// 测试不存在的索引
String nonExistItem = jedis.lindex(LIST_KEY, 5);
assertNull(nonExistItem);
System.out.println("LINDEX operation successful - Non-existent index returns null");
}
@Test
public void testLtrim() {
// 准备测试数据
jedis.rpush(LIST_KEY, "item1", "item2", "item3", "item4");
// 修剪列表
String ltrimResult = jedis.ltrim(LIST_KEY, 0, 1);
assertEquals("OK", ltrimResult);
System.out.println("LTRIM operation successful");
// 验证修剪结果
List<String> items = jedis.lrange(LIST_KEY, 0, -1);
assertEquals(2, items.size());
assertEquals("item1", items.get(0));
assertEquals("item2", items.get(1));
System.out.println("List after trim: " + items);
}
@After
public void cleanup() {
// 清理测试数据
jedis.del(LIST_KEY);
System.out.println("Test data cleaned up");
}
}
4、set类型操作
package cn.kdyzm.jedis.demo;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import redis.clients.jedis.JedisPooled;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;
import static org.junit.Assert.*;
public class RedisSetOperationsTest {
private static final String HOST = "192.168.203.130";
private static final int PORT = 6379;
private JedisPooled jedis = new JedisPooled(HOST, PORT);
private static final String SET_KEY = "testSet";
private static final String SET1_KEY = "set1";
private static final String SET2_KEY = "set2";
@Before
public void setUp() {
// 清空测试用key
jedis.del(SET_KEY, SET1_KEY, SET2_KEY);
}
@After
public void tearDown() {
// 清理测试数据
jedis.del(SET_KEY, SET1_KEY, SET2_KEY);
System.out.println("Test data cleaned up");
}
@Test
public void testSaddAndSmembers() {
// 添加元素
long added = jedis.sadd(SET_KEY, "member1", "member2");
assertEquals(2, added);
System.out.println("SADD operation successful - Members added: " + added);
// 获取所有成员
Set<String> members = jedis.smembers(SET_KEY);
assertNotNull(members);
assertEquals(2, members.size());
assertTrue(members.contains("member1"));
assertTrue(members.contains("member2"));
System.out.println("SMEMBERS operation successful - Members: " + members);
}
@Test
public void testSismember() {
// 准备测试数据
jedis.sadd(SET_KEY, "member1", "member2");
// 判断是否是成员
boolean isMember = jedis.sismember(SET_KEY, "member1");
assertTrue(isMember);
System.out.println("SISMEMBER operation successful - 'member1' exists: " + isMember);
// 测试不存在的成员
boolean notMember = jedis.sismember(SET_KEY, "nonMember");
assertFalse(notMember);
System.out.println("SISMEMBER operation successful - 'nonMember' does not exist");
}
@Test
public void testSrem() {
// 准备测试数据
jedis.sadd(SET_KEY, "member1", "member2", "member3");
// 移除成员
long removed = jedis.srem(SET_KEY, "member1", "member3");
assertEquals(2, removed);
System.out.println("SREM operation successful - Members removed: " + removed);
// 验证移除结果
Set<String> remaining = jedis.smembers(SET_KEY);
assertEquals(1, remaining.size());
assertTrue(remaining.contains("member2"));
System.out.println("Remaining members: " + remaining);
}
@Test
public void testScard() {
// 准备测试数据
jedis.sadd(SET_KEY, "member1", "member2", "member3", "member4");
// 获取集合大小
long size = jedis.scard(SET_KEY);
assertEquals(4, size);
System.out.println("SCARD operation successful - Set size: " + size);
}
@Test
public void testSrandmember() {
// 准备测试数据
jedis.sadd(SET_KEY, "member1", "member2", "member3");
// 随机获取一个成员
String randomMember = jedis.srandmember(SET_KEY);
assertNotNull(randomMember);
assertTrue(randomMember.startsWith("member"));
System.out.println("SRANDMEMBER operation successful - Random member: " + randomMember);
}
@Test
public void testSetOperations() {
// 准备测试数据
jedis.sadd(SET1_KEY, "a", "b", "c");
jedis.sadd(SET2_KEY, "b", "c", "d");
// 交集
Set<String> intersection = jedis.sinter(SET1_KEY, SET2_KEY);
assertNotNull(intersection);
assertEquals(2, intersection.size());
assertTrue(intersection.contains("b"));
assertTrue(intersection.contains("c"));
System.out.println("SINTER operation successful - Intersection: " + intersection);
// 并集
Set<String> union = jedis.sunion(SET1_KEY, SET2_KEY);
assertNotNull(union);
assertEquals(4, union.size());
assertTrue(union.containsAll(new HashSet<>(Arrays.asList("a", "b", "c", "d"))));
System.out.println("SUNION operation successful - Union: " + union);
// 差集
Set<String> difference = jedis.sdiff(SET1_KEY, SET2_KEY);
assertNotNull(difference);
assertEquals(1, difference.size());
assertTrue(difference.contains("a"));
System.out.println("SDIFF operation successful - Difference: " + difference);
}
}
5、zset类型操作
package cn.kdyzm.jedis.demo;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import redis.clients.jedis.JedisPooled;
import redis.clients.jedis.resps.Tuple;
import java.util.List;
import static org.junit.Assert.*;
public class RedisZSetOperationsTest {
private static final String HOST = "192.168.203.130";
private static final int PORT = 6379;
private JedisPooled jedis = new JedisPooled(HOST, PORT);
private static final String ZSET_KEY = "testZSet";
@Before
public void setUp() {
// 清空测试用key
jedis.del(ZSET_KEY);
}
@After
public void tearDown() {
// 清理测试数据
jedis.del(ZSET_KEY);
System.out.println("Test data cleaned up");
}
@Test
public void testZaddAndZscore() {
// 添加成员
long added1 = jedis.zadd(ZSET_KEY, 1.0, "member1");
assertEquals(1, added1);
long added2 = jedis.zadd(ZSET_KEY, 2.0, "member2");
assertEquals(1, added2);
System.out.println("ZADD operations successful");
// 获取成员分数
Double score = jedis.zscore(ZSET_KEY, "member1");
assertEquals(1.0, score, 0.001);
System.out.println("ZSCORE operation successful - Score: " + score);
}
@Test
public void testZrankAndZrevrank() {
// 准备测试数据
jedis.zadd(ZSET_KEY, 1.0, "member1");
jedis.zadd(ZSET_KEY, 2.0, "member2");
jedis.zadd(ZSET_KEY, 3.0, "member3");
// 获取排名(从小到大)
long rank = jedis.zrank(ZSET_KEY, "member1");
assertEquals(0, rank);
System.out.println("ZRANK operation successful - Rank: " + rank);
// 获取排名(从大到小)
long revRank = jedis.zrevrank(ZSET_KEY, "member1");
assertEquals(2, revRank);
System.out.println("ZREVRANK operation successful - Reverse rank: " + revRank);
}
@Test
public void testZrangeAndZrevrange() {
// 准备测试数据
jedis.zadd(ZSET_KEY, 1.0, "member1");
jedis.zadd(ZSET_KEY, 2.0, "member2");
jedis.zadd(ZSET_KEY, 3.0, "member3");
// 获取范围内的成员(从小到大)
List<String> members = jedis.zrange(ZSET_KEY, 0, -1);
assertArrayEquals(new String[]{"member1", "member2", "member3"},
members.toArray(new String[0]));
System.out.println("ZRANGE operation successful - Members: " + members);
// 获取范围内的成员(从大到小)
List<String> revMembers = jedis.zrevrange(ZSET_KEY, 0, -1);
assertArrayEquals(new String[]{"member3", "member2", "member1"},
revMembers.toArray(new String[0]));
System.out.println("ZREVRANGE operation successful - Reverse members: " + revMembers);
}
@Test
public void testZrangeWithScores() {
// 准备测试数据
jedis.zadd(ZSET_KEY, 1.0, "member1");
jedis.zadd(ZSET_KEY, 2.0, "member2");
// 获取范围内的成员(带分数)
List<Tuple> tuples = jedis.zrangeWithScores(ZSET_KEY, 0, -1);
assertEquals(2, tuples.size());
for (Tuple tuple : tuples) {
String element = tuple.getElement();
double score = tuple.getScore();
System.out.println("Member: " + element + ", Score: " + score);
if (element.equals("member1")) {
assertEquals(1.0, score, 0.001);
} else if (element.equals("member2")) {
assertEquals(2.0, score, 0.001);
}
}
System.out.println("ZRANGE WITHSCORES operation successful");
}
@Test
public void testZrangeByScore() {
// 准备测试数据
jedis.zadd(ZSET_KEY, 1.0, "member1");
jedis.zadd(ZSET_KEY, 2.0, "member2");
jedis.zadd(ZSET_KEY, 3.0, "member3");
// 根据分数范围获取成员
List<String> byScore = jedis.zrangeByScore(ZSET_KEY, 1.0, 2.0);
assertEquals(2, byScore.size());
assertTrue(byScore.contains("member1"));
assertTrue(byScore.contains("member2"));
System.out.println("ZRANGEBYSCORE operation successful - Members: " + byScore);
}
@Test
public void testZrem() {
// 准备测试数据
jedis.zadd(ZSET_KEY, 1.0, "member1");
jedis.zadd(ZSET_KEY, 2.0, "member2");
// 移除成员
long removed = jedis.zrem(ZSET_KEY, "member1");
assertEquals(1, removed);
System.out.println("ZREM operation successful - Members removed: " + removed);
// 验证移除结果
Double score = jedis.zscore(ZSET_KEY, "member1");
assertNull(score);
System.out.println("Member removal verification successful");
}
@Test
public void testZcard() {
// 准备测试数据
jedis.zadd(ZSET_KEY, 1.0, "member1");
jedis.zadd(ZSET_KEY, 2.0, "member2");
jedis.zadd(ZSET_KEY, 3.0, "member3");
// 获取集合大小
long size = jedis.zcard(ZSET_KEY);
assertEquals(3, size);
System.out.println("ZCARD operation successful - Set size: " + size);
}
}
6、bitmap类型操作
package cn.kdyzm.jedis.demo;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import redis.clients.jedis.JedisPooled;
import redis.clients.jedis.args.BitOP;
import static org.junit.Assert.*;
public class RedisBitmapOperationsTest {
private static final String HOST = "192.168.203.130";
private static final int PORT = 6379;
private JedisPooled jedis = new JedisPooled(HOST, PORT);
private static final String BITMAP_KEY = "testBitmap";
private static final String BITMAP1_KEY = "bitmap1";
private static final String BITMAP2_KEY = "bitmap2";
private static final String RESULT_KEY = "bitResult";
@Before
public void setUp() {
// 清空测试用key
jedis.del(BITMAP_KEY, BITMAP1_KEY, BITMAP2_KEY, RESULT_KEY);
}
@After
public void tearDown() {
// 清理测试数据
jedis.del(BITMAP_KEY, BITMAP1_KEY, BITMAP2_KEY, RESULT_KEY);
System.out.println("Test data cleaned up");
}
@Test
public void testSetbitAndGetbit() {
// 设置位
boolean prevBit1 = jedis.setbit(BITMAP_KEY, 0, true);
assertFalse(prevBit1); // 之前该位应该是0
System.out.println("SETBIT operation successful - Set bit 0 to 1");
boolean prevBit2 = jedis.setbit(BITMAP_KEY, 1, false);
assertFalse(prevBit2); // 之前该位应该是0
System.out.println("SETBIT operation successful - Set bit 1 to 0");
// 获取位
boolean bit0 = jedis.getbit(BITMAP_KEY, 0);
assertTrue(bit0);
System.out.println("GETBIT operation successful - Bit 0: " + bit0);
boolean bit1 = jedis.getbit(BITMAP_KEY, 1);
assertFalse(bit1);
System.out.println("GETBIT operation successful - Bit 1: " + bit1);
// 测试未设置的位
boolean bit100 = jedis.getbit(BITMAP_KEY, 100);
assertFalse(bit100);
System.out.println("GETBIT operation successful - Unset bit 100: " + bit100);
}
@Test
public void testBitcount() {
// 准备测试数据
jedis.setbit(BITMAP_KEY, 0, true);
jedis.setbit(BITMAP_KEY, 1, false);
jedis.setbit(BITMAP_KEY, 2, true);
jedis.setbit(BITMAP_KEY, 3, true);
jedis.setbit(BITMAP_KEY, 4, false);
// 统计设置为1的位数
long count = jedis.bitcount(BITMAP_KEY);
assertEquals(3, count);
System.out.println("BITCOUNT operation successful - Count: " + count);
// 测试指定范围内的bitcount
long rangeCount = jedis.bitcount(BITMAP_KEY, 0, 1); // 前两个字节
assertEquals(3, rangeCount);
System.out.println("BITCOUNT with range operation successful - Count: " + rangeCount);
}
@Test
public void testBitop() {
// 准备测试数据
jedis.setbit(BITMAP1_KEY, 0, true);
jedis.setbit(BITMAP1_KEY, 1, true);
jedis.setbit(BITMAP1_KEY, 2, false);
jedis.setbit(BITMAP2_KEY, 0, true);
jedis.setbit(BITMAP2_KEY, 1, false);
jedis.setbit(BITMAP2_KEY, 2, true);
// 位运算 AND
long resultLength = jedis.bitop(BitOP.AND, RESULT_KEY, BITMAP1_KEY, BITMAP2_KEY);
assertTrue(resultLength > 0);
System.out.println("BITOP AND operation successful - Result length: " + resultLength);
// 验证结果
assertTrue(jedis.getbit(RESULT_KEY, 0)); // 1 AND 1 = 1
assertFalse(jedis.getbit(RESULT_KEY, 1)); // 1 AND 0 = 0
assertFalse(jedis.getbit(RESULT_KEY, 2)); // 0 AND 1 = 0
System.out.println("BITOP AND verification successful");
// 位运算 OR
resultLength = jedis.bitop(BitOP.OR, RESULT_KEY, BITMAP1_KEY, BITMAP2_KEY);
assertTrue(resultLength > 0);
System.out.println("BITOP OR operation successful - Result length: " + resultLength);
// 验证结果
assertTrue(jedis.getbit(RESULT_KEY, 0)); // 1 OR 1 = 1
assertTrue(jedis.getbit(RESULT_KEY, 1)); // 1 OR 0 = 1
assertTrue(jedis.getbit(RESULT_KEY, 2)); // 0 OR 1 = 1
System.out.println("BITOP OR verification successful");
// 位运算 XOR
resultLength = jedis.bitop(BitOP.XOR, RESULT_KEY, BITMAP1_KEY, BITMAP2_KEY);
assertTrue(resultLength > 0);
System.out.println("BITOP XOR operation successful - Result length: " + resultLength);
// 验证结果
assertFalse(jedis.getbit(RESULT_KEY, 0)); // 1 XOR 1 = 0
assertTrue(jedis.getbit(RESULT_KEY, 1)); // 1 XOR 0 = 1
assertTrue(jedis.getbit(RESULT_KEY, 2)); // 0 XOR 1 = 1
System.out.println("BITOP XOR verification successful");
// 位运算 NOT (只需要一个源key)
resultLength = jedis.bitop(BitOP.NOT, RESULT_KEY, BITMAP1_KEY);
assertTrue(resultLength > 0);
System.out.println("BITOP NOT operation successful - Result length: " + resultLength);
// 验证结果
assertFalse(jedis.getbit(RESULT_KEY, 0)); // NOT 1 = 0
assertFalse(jedis.getbit(RESULT_KEY, 1)); // NOT 1 = 0
assertTrue(jedis.getbit(RESULT_KEY, 2)); // NOT 0 = 1
System.out.println("BITOP NOT verification successful");
}
}
7、HyperLogLog类型操作
package cn.kdyzm.jedis.demo;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import redis.clients.jedis.JedisPooled;
import static org.junit.Assert.*;
public class RedisHyperLogLogTest {
private static final String HOST = "192.168.203.130";
private static final int PORT = 6379;
private JedisPooled jedis = new JedisPooled(HOST, PORT);
private static final String HLL_KEY = "testHLL";
private static final String HLL2_KEY = "testHLL2";
private static final String MERGED_KEY = "mergedHLL";
@Before
public void setUp() {
// 清空测试用key
jedis.del(HLL_KEY, HLL2_KEY, MERGED_KEY);
}
@After
public void tearDown() {
// 清理测试数据
jedis.del(HLL_KEY, HLL2_KEY, MERGED_KEY);
System.out.println("Test data cleaned up");
}
@Test
public void testPfaddAndPfcount() {
// 添加元素
long added = jedis.pfadd(HLL_KEY, "item1", "item2", "item3");
assertEquals(1, added);
System.out.println("PFADD operation successful - Elements added");
// 获取基数估计
long count = jedis.pfcount(HLL_KEY);
assertEquals(3, count);
System.out.println("PFCOUNT operation successful - Estimated count: " + count);
// 添加重复元素
long dupAdded = jedis.pfadd(HLL_KEY, "item1", "item2");
assertEquals(0, dupAdded);
System.out.println("PFADD with duplicates - No new elements added");
// 验证基数不变
long sameCount = jedis.pfcount(HLL_KEY);
assertEquals(3, sameCount);
System.out.println("PFCOUNT after duplicates - Same count: " + sameCount);
}
@Test
public void testPfmerge() {
// 准备测试数据
jedis.pfadd(HLL_KEY, "item1", "item2", "item3");
jedis.pfadd(HLL2_KEY, "item3", "item4", "item5");
// 合并多个HLL
String mergeResult = jedis.pfmerge(MERGED_KEY, HLL_KEY, HLL2_KEY);
assertEquals("OK", mergeResult);
System.out.println("PFMERGE operation successful");
// 获取合并后的基数估计
long mergedCount = jedis.pfcount(MERGED_KEY);
assertTrue(mergedCount >= 5 && mergedCount <= 6); // HyperLogLog有约0.81%误差
System.out.println("PFCOUNT after merge - Estimated count: " + mergedCount);
// 验证原始HLL不变
long originalCount = jedis.pfcount(HLL_KEY);
assertEquals(3, originalCount);
System.out.println("Original HLL count unchanged: " + originalCount);
}
@Test
public void testEmptyHLL() {
// 测试空HLL
long emptyCount = jedis.pfcount(HLL_KEY);
assertEquals(0, emptyCount);
System.out.println("PFCOUNT on empty HLL returns 0");
// 添加空元素
long emptyAdd = jedis.pfadd(HLL_KEY);
assertEquals(1, emptyAdd);
System.out.println("PFADD with no elements returns 1");
}
@Test
public void testHLLAccuracy() {
// 测试HLL的基数估计准确性
int testSize = 10000;
String[] elements = new String[testSize];
// 生成10000个唯一元素
for (int i = 0; i < testSize; i++) {
elements[i] = "element-" + i;
}
// 添加元素到HLL
jedis.pfadd(HLL_KEY, elements);
// 获取基数估计
long estimatedCount = jedis.pfcount(HLL_KEY);
System.out.println("Actual count: " + testSize + ", Estimated count: " + estimatedCount);
// 计算误差率 (HLL标准误差约0.81%)
double errorRate = Math.abs(estimatedCount - testSize) * 100.0 / testSize;
System.out.printf("Error rate: %.2f%%\n", errorRate);
// 验证误差在合理范围内
assertTrue(errorRate < 2.0); // 放宽到2%以应对测试波动
}
}
8、GEO类型操作
package cn.kdyzm.jedis.demo;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import redis.clients.jedis.*;
import redis.clients.jedis.args.GeoUnit;
import redis.clients.jedis.params.GeoRadiusParam;
import redis.clients.jedis.resps.GeoRadiusResponse;
import java.util.List;
import static org.junit.Assert.*;
public class RedisGeoOperationsTest {
private static final String HOST = "192.168.203.130";
private static final int PORT = 6379;
private JedisPooled jedis = new JedisPooled(HOST, PORT);
private static final String GEO_KEY = "cities";
// 城市坐标常量
private static final double BEIJING_LON = 116.405285;
private static final double BEIJING_LAT = 39.904989;
private static final double SHANGHAI_LON = 121.474490;
private static final double SHANGHAI_LAT = 31.230416;
private static final double GUANGZHOU_LON = 113.264385;
private static final double GUANGZHOU_LAT = 23.129112;
@Before
public void setUp() {
// 清空测试数据
jedis.del(GEO_KEY);
}
@After
public void tearDown() {
// 清理测试数据
jedis.del(GEO_KEY);
System.out.println("Test data cleaned up");
}
@Test
public void testGeoaddAndGeopos() {
// 添加地理位置
long addedBeijing = jedis.geoadd(GEO_KEY, BEIJING_LON, BEIJING_LAT, "Beijing");
assertEquals(1, addedBeijing);
long addedShanghai = jedis.geoadd(GEO_KEY, SHANGHAI_LON, SHANGHAI_LAT, "Shanghai");
assertEquals(1, addedShanghai);
System.out.println("GEOADD operations successful");
// 获取地理位置
List<GeoCoordinate> coordinates = jedis.geopos(GEO_KEY, "Beijing", "Shanghai", "NonExistCity");
assertNotNull(coordinates);
assertEquals(3, coordinates.size());
// 验证北京坐标
GeoCoordinate beijingCoord = coordinates.get(0);
assertNotNull(beijingCoord);
assertEquals(BEIJING_LON, beijingCoord.getLongitude(), 0.00001);
assertEquals(BEIJING_LAT, beijingCoord.getLatitude(), 0.00001);
// 验证上海坐标
GeoCoordinate shanghaiCoord = coordinates.get(1);
assertNotNull(shanghaiCoord);
assertEquals(SHANGHAI_LON, shanghaiCoord.getLongitude(), 0.00001);
assertEquals(SHANGHAI_LAT, shanghaiCoord.getLatitude(), 0.00001);
// 验证不存在的城市
assertNull(coordinates.get(2));
System.out.println("GEOPOS operation successful");
System.out.println("Beijing coordinates: " + beijingCoord);
System.out.println("Shanghai coordinates: " + shanghaiCoord);
}
@Test
public void testGeodist() {
// 准备测试数据
jedis.geoadd(GEO_KEY, BEIJING_LON, BEIJING_LAT, "Beijing");
jedis.geoadd(GEO_KEY, SHANGHAI_LON, SHANGHAI_LAT, "Shanghai");
jedis.geoadd(GEO_KEY, GUANGZHOU_LON, GUANGZHOU_LAT, "Guangzhou");
// 计算距离(千米)
Double distance = jedis.geodist(GEO_KEY, "Beijing", "Shanghai", GeoUnit.KM);
assertNotNull(distance);
assertTrue(distance > 1000 && distance < 1200); // 北京到上海实际距离约1064公里
System.out.println("GEODIST operation successful - Distance: " + distance + " km");
// 计算距离(米)
Double distanceMeters = jedis.geodist(GEO_KEY, "Beijing", "Shanghai", GeoUnit.M);
assertEquals(distance * 1000, distanceMeters, 100); // 允许100米误差
// 测试不存在的城市
Double nonExistDist = jedis.geodist(GEO_KEY, "Beijing", "NonExistCity", GeoUnit.KM);
assertNull(nonExistDist);
System.out.println("GEODIST with non-existent city returns null");
}
@Test
public void testGeoradius() {
// 准备测试数据
jedis.geoadd(GEO_KEY, BEIJING_LON, BEIJING_LAT, "Beijing");
jedis.geoadd(GEO_KEY, SHANGHAI_LON, SHANGHAI_LAT, "Shanghai");
jedis.geoadd(GEO_KEY, GUANGZHOU_LON, GUANGZHOU_LAT, "Guangzhou");
// 查找附近的位置(500公里范围内)
List<GeoRadiusResponse> nearby = jedis.georadius(GEO_KEY, BEIJING_LON, BEIJING_LAT, 500, GeoUnit.KM);
assertNotNull(nearby);
assertEquals(1, nearby.size()); // 只有北京自己在500公里范围内
assertEquals("Beijing", nearby.get(0).getMemberByString());
System.out.println("GEORADIUS operation successful (500km) - Nearby cities: " + nearby.size());
// 查找附近的位置(1500公里范围内)
List<GeoRadiusResponse> widerNearby = jedis.georadius(GEO_KEY, BEIJING_LON, BEIJING_LAT, 1500, GeoUnit.KM);
assertNotNull(widerNearby);
assertEquals(2, widerNearby.size()); // 北京和上海
System.out.println("GEORADIUS operation successful (1500km) - Nearby cities: " + widerNearby.size());
// 测试带选项的GEORADIUS
GeoRadiusParam param = new GeoRadiusParam()
.withCoord()
.withDist()
.sortAscending()
.count(2);
List<GeoRadiusResponse> nearbyWithParams = jedis.georadius(
GEO_KEY, BEIJING_LON, BEIJING_LAT, 2000, GeoUnit.KM, param);
assertNotNull(nearbyWithParams);
assertEquals(2, nearbyWithParams.size());
assertTrue(nearbyWithParams.get(0).getDistance() <= nearbyWithParams.get(1).getDistance());
System.out.println("GEORADIUS with params successful - Results: " + nearbyWithParams);
}
@Test
public void testGeoradiusByMember() {
// 准备测试数据
jedis.geoadd(GEO_KEY, BEIJING_LON, BEIJING_LAT, "Beijing");
jedis.geoadd(GEO_KEY, SHANGHAI_LON, SHANGHAI_LAT, "Shanghai");
jedis.geoadd(GEO_KEY, GUANGZHOU_LON, GUANGZHOU_LAT, "Guangzhou");
// 以北京为中心查找附近位置
List<GeoRadiusResponse> nearby = jedis.georadiusByMember(
GEO_KEY, "Beijing", 1500, GeoUnit.KM);
assertNotNull(nearby);
assertEquals(2, nearby.size()); // 北京和上海
System.out.println("GEORADIUSBYMEMBER operation successful - Nearby cities: " + nearby.size());
// 测试带选项的GEORADIUSBYMEMBER
GeoRadiusParam param = new GeoRadiusParam()
.withCoord()
.withDist()
.sortDescending();
List<GeoRadiusResponse> nearbyWithParams = jedis.georadiusByMember(
GEO_KEY, "Beijing", 2000, GeoUnit.KM, param);
assertNotNull(nearbyWithParams);
assertEquals(3, nearbyWithParams.size()); // 北京、上海、广州
assertTrue(nearbyWithParams.get(0).getDistance() >= nearbyWithParams.get(1).getDistance());
System.out.println("GEORADIUSBYMEMBER with params successful - Results: " + nearbyWithParams);
}
@Test
public void testGeoHash() {
// 准备测试数据
jedis.geoadd(GEO_KEY, BEIJING_LON, BEIJING_LAT, "Beijing");
jedis.geoadd(GEO_KEY, SHANGHAI_LON, SHANGHAI_LAT, "Shanghai");
// 获取Geohash
List<String> hashes = jedis.geohash(GEO_KEY, "Beijing", "Shanghai");
assertNotNull(hashes);
assertEquals(2, hashes.size());
// 验证Geohash格式
assertTrue(hashes.get(0).matches("^[0-9a-z]+$")); // 北京
assertTrue(hashes.get(1).matches("^[0-9a-z]+$")); // 上海
System.out.println("GEOHASH operation successful");
System.out.println("Beijing geohash: " + hashes.get(0));
System.out.println("Shanghai geohash: " + hashes.get(1));
}
}
9、消息队列操作
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPubSub;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import static org.junit.Assert.*;
public class RedisPubSubTest {
private static final String HOST = "192.168.203.130";
private static final int PORT = 6379;
private Jedis publisherJedis;
private Jedis subscriberJedis;
private static final String CHANNEL = "testChannel";
private static final String MESSAGE = "testMessage";
@Before
public void setUp() {
// 创建独立的连接用于发布和订阅
publisherJedis = new Jedis(HOST, PORT);
subscriberJedis = new Jedis(HOST, PORT);
}
@After
public void tearDown() {
if (publisherJedis != null) {
publisherJedis.close();
}
if (subscriberJedis != null) {
subscriberJedis.close();
}
System.out.println("Resources cleaned up");
}
@Test
public void testPubSub() throws InterruptedException {
// 使用CountDownLatch等待消息接收
CountDownLatch messageReceived = new CountDownLatch(1);
// 创建订阅者
JedisPubSub jedisPubSub = new JedisPubSub() {
@Override
public void onMessage(String channel, String message) {
System.out.println("Received message: " + message + " from channel: " + channel);
assertEquals(CHANNEL, channel);
assertEquals(MESSAGE, message);
messageReceived.countDown();
}
@Override
public void onSubscribe(String channel, int subscribedChannels) {
System.out.println("Subscribed to channel: " + channel);
}
};
// 在单独线程中订阅
new Thread(() -> {
System.out.println("Starting subscriber...");
subscriberJedis.subscribe(jedisPubSub, CHANNEL);
}).start();
// 等待订阅成功
Thread.sleep(1000);
// 发布消息
long subscribers = publisherJedis.publish(CHANNEL, MESSAGE);
assertTrue(subscribers > 0);
System.out.println("Published message to " + subscribers + " subscribers");
// 等待消息接收(最多等待5秒)
boolean received = messageReceived.await(5, TimeUnit.SECONDS);
assertTrue("Message not received within timeout", received);
// 取消订阅
jedisPubSub.unsubscribe();
}
@Test
public void testPatternSubscribe() throws InterruptedException {
// 使用CountDownLatch等待消息接收
CountDownLatch messageReceived = new CountDownLatch(1);
String patternChannel = "test.*";
String specificChannel = "test.pattern";
// 创建模式订阅者
JedisPubSub jedisPubSub = new JedisPubSub() {
@Override
public void onPMessage(String pattern, String channel, String message) {
System.out.println("Received message: " + message +
" from channel: " + channel +
" matching pattern: " + pattern);
assertEquals(patternChannel, pattern);
assertEquals(specificChannel, channel);
assertEquals(MESSAGE, message);
messageReceived.countDown();
}
@Override
public void onPSubscribe(String pattern, int subscribedChannels) {
System.out.println("Subscribed to pattern: " + pattern);
}
};
// 在单独线程中订阅模式
new Thread(() -> {
System.out.println("Starting pattern subscriber...");
subscriberJedis.psubscribe(jedisPubSub, patternChannel);
}).start();
// 等待订阅成功
Thread.sleep(1000);
// 发布消息到匹配模式的频道
long subscribers = publisherJedis.publish(specificChannel, MESSAGE);
assertTrue(subscribers > 0);
System.out.println("Published message to " + subscribers + " subscribers");
// 等待消息接收(最多等待5秒)
boolean received = messageReceived.await(5, TimeUnit.SECONDS);
assertTrue("Message not received within timeout", received);
// 取消订阅
jedisPubSub.punsubscribe();
}
@Test
public void testMultipleChannels() throws InterruptedException {
String channel1 = "channel1";
String channel2 = "channel2";
CountDownLatch messagesReceived = new CountDownLatch(2);
// 创建订阅者
JedisPubSub jedisPubSub = new JedisPubSub() {
@Override
public void onMessage(String channel, String message) {
System.out.println("Received message from channel: " + channel);
messagesReceived.countDown();
}
};
// 在单独线程中订阅多个频道
new Thread(() -> {
System.out.println("Starting multi-channel subscriber...");
subscriberJedis.subscribe(jedisPubSub, channel1, channel2);
}).start();
// 等待订阅成功
Thread.sleep(1000);
// 发布消息到两个频道
publisherJedis.publish(channel1, MESSAGE);
publisherJedis.publish(channel2, MESSAGE);
// 等待消息接收(最多等待5秒)
boolean received = messagesReceived.await(5, TimeUnit.SECONDS);
assertTrue("Not all messages received within timeout", received);
// 取消订阅
jedisPubSub.unsubscribe();
}
}
10、事务操作
package cn.kdyzm.jedis.demo;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.Transaction;
import redis.clients.jedis.exceptions.JedisDataException;
import java.util.List;
import static org.junit.Assert.*;
public class RedisTransactionTest {
private static final String HOST = "192.168.203.130";
private static final int PORT = 6379;
private Jedis jedis;
private static final String KEY1 = "txKey1";
private static final String KEY2 = "txKey2";
private static final String COUNTER = "txCounter";
@Before
public void setUp() {
jedis = new Jedis(HOST, PORT);
// 清空测试数据
jedis.del(KEY1, KEY2, COUNTER);
}
@After
public void tearDown() {
if (jedis != null) {
jedis.close();
}
System.out.println("Resources cleaned up");
}
@Test
public void testSuccessfulTransaction() {
// 开启事务
Transaction tx = jedis.multi();
System.out.println("Transaction started");
// 执行命令
tx.set(KEY1, "value1");
tx.set(KEY2, "value2");
tx.incr(COUNTER);
System.out.println("Commands added to transaction");
// 执行事务
List<Object> results = tx.exec();
assertNotNull(results);
assertEquals(3, results.size());
System.out.println("Transaction executed successfully");
// 验证结果
assertEquals("OK", results.get(0));
assertEquals("OK", results.get(1));
assertEquals(1L, results.get(2));
// 验证数据
assertEquals("value1", jedis.get(KEY1));
assertEquals("value2", jedis.get(KEY2));
assertEquals("1", jedis.get(COUNTER));
System.out.println("Data verification successful");
}
@Test
public void testDiscardedTransaction() {
// 开启事务
Transaction tx = jedis.multi();
System.out.println("Transaction started");
// 执行命令
tx.set(KEY1, "value1");
tx.set(KEY2, "value2");
System.out.println("Commands added to transaction");
// 放弃事务
String discardResult = tx.discard();
assertEquals("OK", discardResult);
System.out.println("Transaction discarded successfully");
// 验证数据未被修改
assertNull(jedis.get(KEY1));
assertNull(jedis.get(KEY2));
System.out.println("Data remains unchanged after discard");
}
@Test(expected = IllegalStateException.class)
public void testWatchAndConflict() {
// 监视键
jedis.watch(KEY1);
System.out.println("Watching key: " + KEY1);
// 开启事务
Transaction tx = jedis.multi();
System.out.println("Transaction started");
// 尝试修改被监视的键
tx.set(KEY1, "transactionValue");
System.out.println("Attempting to modify watched key in transaction");
// 在事务外修改被监视的键,此处应该抛出IllegalStateException异常
jedis.set(KEY1, "externalChange");
System.out.println("Key modified outside transaction");
// 执行事务(应该失败)
tx.exec();
System.out.println("This line should not be reached");
}
@Test
public void testWatchAndNoConflict() {
// 监视键
jedis.watch(KEY1);
System.out.println("Watching key: " + KEY1);
// 开启事务
Transaction tx = jedis.multi();
System.out.println("Transaction started");
// 修改被监视的键
tx.set(KEY1, "transactionValue");
System.out.println("Modifying watched key in transaction");
// 执行事务(应该成功,因为没有冲突)
List<Object> results = tx.exec();
assertNotNull(results);
assertEquals(1, results.size());
assertEquals("OK", results.get(0));
System.out.println("Transaction executed successfully with no conflicts");
// 验证数据
assertEquals("transactionValue", jedis.get(KEY1));
System.out.println("Data verification successful");
}
@Test
public void testTransactionWithError() {
// 设置一个非数字值
jedis.set(COUNTER, "notANumber");
// 开启事务
Transaction tx = jedis.multi();
System.out.println("Transaction started");
// 执行命令(包含一个会失败的命令)
tx.set(KEY1, "value1");
tx.incr(COUNTER); // 这会失败,但是会继续执行剩下的命令
tx.set(KEY2, "value2");
System.out.println("Commands added to transaction (one will fail)");
// 执行事务
try {
tx.exec();
} catch (JedisDataException e) {
System.out.println("Transaction failed as expected: " + e.getMessage());
}
// 验证部分命令未执行
assertNotNull(jedis.get(KEY1));
assertNotNull(jedis.get(KEY2));
assertEquals("notANumber", jedis.get(COUNTER));
System.out.println("No commands were executed due to transaction failure");
}
}
四、注意事项
jedis在实际开发中直接使用的可能性比较低,由于springboot官方有redis spring boot starter,我们一般习惯性的基于该组件操作redis。
END.
注意:本文归作者所有,未经作者允许,不得转载