使用数据库的时候,我们可以用JDBC来实现mysql数据库与java程序之间的通信,为了提高通信效率,我们有了数据库连接池比如druid等等。而我们想通过Java程序控制redis,同样可以借助一些工具来实现,这就是redis客户端,常见的有Jedis、lettuce、redisson等等,这几个工具各有优缺点,而且应用都比较多,所以我们分三篇逐步来看。
Jedis 是一个功能强大、易用性高的 Redis 客户端库,适用于 Java 开发人员在项目中连接和操作 Redis 服务器。接下来,将介绍 Jedis 的使用方法和示例。
目录
Jedis 是一个流行的 Java 编写的 Redis 客户端库,它提供了连接和操作 Redis 服务器的功能。Jedis具有以下特点:
如果我们想在通过java 来管理redis,可以按照下面的方式进行:
首先,下载 Jedis
Jedis 的源代码托管在 Github 上,可以直接从这里获取。也可以通过 Maven 或 Gradle 添加 Jedis 的依赖。在?pom.xml
?文件中添加以下内容:
对于 Maven,添加以下依赖:
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
<version>2.10.0</version>
</dependency>
版本一直在更新,Jedis是Java语言的Redis客户端库,它的版本号可以通过查看Jedis的Maven依赖或者JAR包的版本信息来获取。以下是一些常见的Jedis版本号范例:
这里我们使用应用比较多的2.10.0版本。
然后,配置Redis服务器的地址和端口
在 Java 程序中,需要提供 Redis 服务器的地址和端口,以便 Jedis 能够连接到 Redis。以下是一段示例代码:
public static void main(String[] args) {
Jedis jedis = new Jedis("localhost", 6379);
// 测试连接
System.out.println("Connection Successful");
System.out.println("Server is running: " + jedis.ping());
}
然后执行,可以看到如下信息,就说明已经安装成功了:
Connection Successful
Server is running: PONG
在这里,我们创建了一个新的 Jedis 对象,然后提供了 Redis 服务器的地址(“localhost”)和默认端口(6379)。然后,调用?jedis.ping()
?来测试与 Redis 服务器的连接。
如果 Redis 服务器需要密码验证,可以在连接时添加密码参数:?
Jedis jedis = new Jedis("localhost", 6379);
jedis.auth("password");
使用的时候,将上面的“password” 换成你的 Redis 服务器的密码。
配置完成后,就可以通过 Jedis 连接和操作 Redis 服务器了。在实际工程中,我们会将连接信息写在配置文件里来集中管理,这里为了简单 ,我们直接用程序写死了。
接下来,将介绍 Jedis 的常用操作方法和示例。
以下是如何使用 Jedis 的?set
?和?get
?方法来设置和获取字符串类型的键值对的例子:
jedis.set("mykey", "海涛");
String value = jedis.get("mykey");
System.out.println("Stored string in redis:: " + value);
输出结果为:
Stored string in redis:: 海涛
可以使用?exists
?方法来检查一个键是否存在。以下是一个例子:
// 判断是否存在
boolean keyExists = jedis.exists("mykey");
System.out.println("Does key 'mykey' exists? " + keyExists);
可以使用?del
?方法来删除一个键。以下是一个例子:
jedis.del("mykey");
此时如果我们再使用上面的代码判断key是否存在就会发现此时返回的是 false。
可以使用?expire
?方法来为一个键设置过期时间(以秒为单位)。以下是一个例子:
jedis.set("mykey", "海涛");
jedis.expire("mykey", 60); // 这个键将在60秒后过期
我们在前面通过redis-cli客户端来操作redis的几种类型,接下来,我们就看一下如何通过jedis来操作这些类型。
字符串类型是最简单的方式,我们可以直接使用set来设置一个,也可以使用mset来设置多个,例如:
jedis.set("key", "海涛"); // 设置字符串类型的键值对
String value = jedis.get("key"); // 获取字符串类型的键对应的值
// 批量设置字符串类型的键值对
jedis.mset("key1", "海涛", "key2", "江涛");
// 批量获取字符串类型的键对应的值
List<String> values = jedis.mget("key1", "key2");
输出结果:
value:海涛
values=[海涛, 江涛]
jedis.set("mykey", "myvalue");
String value = jedis.get("mykey");
System.out.println("Stored string in redis:: " + value);
jedis.mset("key1", "海涛", "key2", "江涛");
List<String> values = jedis.mget("key1", "key2");
我们可以通过jedis的api接口来调用,名称与命令非常类似:
public static void testList( Jedis jedis){
// 添加元素到列表头部和尾部
jedis.lpush("list", "element1", "element2"); // 头部添加
jedis.rpush("list", "element3", "element4"); // 尾部添加
// 获取列表的长度
Long length = jedis.llen("list");
System.out.println(length);
// 获取指定范围内的元素
List<String> elements = jedis.lrange("list", 0, -1); // 获取所有元素
System.out.println(elements);
}
jedis.lpush("mylist", "element1", "element2");
jedis.rpush("mylist", "element3");
Long length = jedis.llen("mylist");
List<String> elements = jedis.lrange("mylist", 0, -1);
集合类型是一种非常常见的类型,基本使用方式如下:
public static void testSet(Jedis jedis) {
// 添加元素到集合
jedis.sadd("set", "element1", "element2");
// 获取集合的大小
Long size = jedis.scard("set");
System.out.println("size=" + size);
// 判断元素是否存在于集合中
boolean exists = jedis.sismember("set", "element1");
System.out.println(exists);
}
jedis.sadd("myset", "element1", "element2");
Long size = jedis.scard("myset");
boolean exists = jedis.sismember("myset", "element1");
hash是实现缓存的重要类型,使用jedis操作哈希的代码如下:
// 设置和获取哈希字段的值
jedis.hset("hash", "field1", "value1");
String value = jedis.hget("hash", "field1");
// 获取哈希中所有字段和值
Map<String, String> hash = jedis.hgetAll("hash");
jedis.hset("myhash", "field1", "value1");
String value = jedis.hget("myhash", "field1");
Map<String, String> allFieldsAndValues = jedis.hgetAll("myhash");
使用jedis进行有序集合的操作如下:
// 添加元素到有序集合
jedis.zadd("zset", 1, "member1");
jedis.zadd("zset", 2, "member2");
// 获取有序集合的成员和分数
Set<Tuple> membersWithScores = jedis.zrangeWithScores("zset", 0, -1);
// 根据分数范围获取有序集合的成员
Set<String> membersInRange = jedis.zrangeByScore("zset", 1, 2);
jedis.zadd("myzset", 1, "element1");
jedis.zadd("myzset", 2, "element2");
Set<String> elements = jedis.zrange("myzset", 0, -1);
Set<String> elements = jedis.zrangeByScore("myzset", 1, 2);
Jedis是Redis的Java客户端库,它提供了丰富的API来操作Redis数据库。在Jedis中,可以使用事务操作来执行一系列的Redis命令,并保证这些命令的原子性。
在 Jedis 中,可以使用 multi() 方法来开启一个事务,然后使用 exec() 方法来提交事务。在这两个方法之间的所有命令都会被放入事务队列中,等待 exec() 方法的调用。
Transaction t = jedis.multi();
t.set("foo", "bar");
t.exec();
Jedis 提供了?discard()
?方法来回滚事务。注意,只有当在调用?multi()
?方法之后,还没有调用?exec()
?方法之前,才可以调用?discard()
?方法。
Transaction t = jedis.multi();
t.set("foo", "bar");
t.discard();
watch()
?方法可以用来监视一个或多个键,如果在事务执行之前这些键被其他命令所改变,那么事务将被打断。
jedis.watch("foo");
Transaction t = jedis.multi();
t.set("foo", "bar");
t.exec();
Jedis是 提供了丰富的功能来与Redis进行交互。其中,发布订阅是Jedis库的一项重要功能,允许客户端在订阅频道时接收发布到频道的消息。
在 Jedis 中,可以使用 publish() 方法来发布消息。第一个参数是频道的名称,第二个参数是消息的内容。
Jedis jedis = new Jedis("localhost", 6379);
String channel = "myChannel";
String message = "Hello subscribers!";
jedis.publish(channel, message);
jedis.close();
订阅消息需要创建一个?JedisPubSub
?对象,并重写它的?onMessage()
?方法。然后使用?subscribe()
?方法来订阅一个或多个频道。
Jedis jedis = new Jedis("localhost", 6379);
JedisPubSub jedisPubSub = new JedisPubSub() {
@Override
public void onMessage(String channel, String message) {
System.out.println("Received message: " + message + " from channel: " + channel);
}
};
String channel = "myChannel";
jedis.subscribe(jedisPubSub, channel);
jedis.close();
定义了一个JedisPubSub对象,该对象覆盖了onMessage方法,用于处理接收到的消息。然后,指定要订阅的频道名称,并使用subscribe方法进行订阅。
需要注意的是,subscribe方法是阻塞的,它会一直等待直到接收到消息。因此,在实际应用中,通常会将订阅操作放在单独的线程中进行,以避免阻塞主线程。
在订阅消息后,如果需要取消订阅,可以使用unsubscribe方法。
jedis.unsubscribe(jedisPubSub, channel);
Jedis提供了管道(pipeline)操作来批量执行多个命令,以减少网络延迟和提高性能。管道操作允许在客户端一次性发送多个命令到Redis服务器,并在服务器端依次执行这些命令,最后将结果一次性返回给客户端。
使用Jedis进行管道操作的步骤如下:
使用Jedis管道的简单示例:
Pipeline pipeline = jedis.pipelined();
for (int i = 0; i < 100; i++) {
pipeline.set("key" + i, "value" + i);
}
pipeline.sync();
创建了一个Pipeline对象,然后连续调用了100次set()
方法。调用sync()
方法会将之前调用的所有命令一次性发送到Redis服务器。
管道中的命令不保证原子性,也就是说,如果管道中的某个命令执行失败,不会影响到其他命令的执行。如果需要保证一组命令的原子性,应该使用Redis的事务功能。
此外,管道中的命令默认是不会立即返回结果的,如果需要获取命令的返回结果,可以使用syncAndReturnAll()方法来替代sync()方法。这将返回一个List,其中包含了管道中每个命令的返回结果。
使用Jedis进行管道操作的示例代码
Jedis jedis = new Jedis("localhost", 6379);
Pipeline pipeline = jedis.pipelined();
// 执行多个命令
pipeline.set("key1", "value1");
pipeline.get("key1");
pipeline.hset("hashKey", "field1", "value2");
// 提交管道
List<Object> results = pipeline.syncAndReturnAll();
// 处理结果
for (Object result : results) {
System.out.println(result);
}
// 关闭Jedis对象
jedis.close();
创建了一个Jedis对象,并通过调用pipelined()方法开启了管道。执行三个命令:设置一个字符串类型的键值对、获取该键的值,以及设置一个哈希类型的字段值对。最后,通过调用syncAndReturnAll()方法提交管道操作,并返回了所有命令的结果。
可以通过遍历结果列表来处理每个命令的返回值。最后,记得关闭Jedis对象以释放资源。
使用管道操作可以显著提高性能,特别是在需要批量执行多个命令时。然而,需要注意的是,管道操作是无法使用事务的。如果需要支持事务操作,可以考虑使用Redis的事务功能。
在实际使用中,通常会使用连接池来管理Jedis连接。Jedis的连接池使用Apache Commons Pool2实现。
基本过程是:
创建了一个JedisPoolConfig对象,并设置了各种连接池参数。然后使用这个配置对象,以及Redis服务器的地址和端口,创建了一个JedisPool对象。
在需要使用Jedis连接的地方,使用getResource()方法从连接池中获取一个Jedis连接。这个连接使用完毕后,Jedis会自动将其归还到连接池中。
注意,在获取Jedis连接时,使用了Java的try-with-resources语句,这样可以确保无论是否发生异常,Jedis连接都会被正确关闭。
配置和使用Jedis连接池的示例:
1.配置连接池参数:
JedisPoolConfig poolConfig = new JedisPoolConfig();
// 最大连接数
poolConfig.setMaxTotal(128);
// 最大空闲连接数
poolConfig.setMaxIdle(64);
// 最小空闲连接数
poolConfig.setMinIdle(10);
// 获取连接时的最大等待毫秒数
poolConfig.setMaxWaitMillis(2000);
// 在获取连接的时候检查有效性
poolConfig.setTestOnBorrow(true);
2.创建Jedis连接池:
JedisPool jedisPool = new JedisPool(poolConfig, "localhost", 6379);
从连接池中获取Jedis连接的代码如下:
try (Jedis jedis = jedisPool.getResource()) {
// 使用Jedis连接执行命令
jedis.set("key", "value");
System.out.println(jedis.get("key"));
} catch (Exception e) {
// 异常处理
}
处理Jedis连接或命令执行异常的一般方法是使用try/catch块来捕获并处理这些异常。
在使用Jedis时,我们需要对连接异常和命令执行异常进行适当的处理。
在创建连接池对象时,可以设置连接超时时间和读写超时时间,以及设置连接失败重试次数。
在获取Jedis连接对象时,可以捕获JedisConnectionException异常来处理连接异常,例如重新获取连接或进行其他操作。
在使用Jedis连接对象进行Redis操作时,如果发生连接异常,可以捕获JedisConnectionException异常,并根据具体情况进行处理,例如重新获取连接或进行其他操作。
示例代码如下:
JedisPoolConfig config = new JedisPoolConfig();
config.setMaxTotal(100);
config.setMaxIdle(50);
config.setMinIdle(10);
config.setTestOnBorrow(true);
config.setTestOnReturn(true);
JedisPool jedisPool = new JedisPool(config, "localhost", 6379, 5000, "password");
Jedis jedis = null;
try {
jedis = jedisPool.getResource();
// 进行Redis操作
} catch (JedisConnectionException e) {
// 处理连接异常,例如重新获取连接或进行其他操作
} finally {
if (jedis != null) {
jedis.close();
}
}
Jedis连接异常通常是由于网络问题或者Redis服务器问题引起的。当尝试获取一个Jedis连接,或者使用一个已经获取的连接进行操作时,都可能触发这种异常。
当尝试执行一个无效的Redis命令,或者传递给命令的参数不正确时,Jedis会抛出一个JedisDataException。
Jedis jedis = new Jedis("localhost", 6379);
try {
jedis.hset("key", "field", "value");
} catch (JedisDataException e) {
// 命令执行异常处理
} finally {
jedis.close();
}
如果hset命令的参数不正确,例如key不存在,或者field已经存在,那么会抛出一个JedisDataException。需要捕获并处理这个异常。
示例代码如下:
try {
jedis.set("key", "value");
} catch (JedisDataException e) {
// 处理命令执行异常,例如命令格式不正确或参数类型不匹配等
} catch (JedisException e) {
// 处理命令执行异常,例如Redis服务器出现问题或连接被关闭等
}
使用连接池:在并发环境下,频繁地建立和释放连接会消耗大量的系统资源,影响应用性能。Jedis提供了连接池功能,可以复用已经建立的连接,减少连接的开销。
JedisPool pool = new JedisPool(new JedisPoolConfig(), "localhost");
Jedis jedis = null;
try {
jedis = pool.getResource();
// 使用jedis进行Redis操作
} finally {
if (jedis != null) {
jedis.close(); // 实际上是将连接返还给连接池,而不是关闭连接
}
}
批量执行命令:如果需要执行多个Redis命令,那么可以使用Jedis的管道(Pipeline)功能,一次性发送所有命令,然后一次性接收所有结果。这样可以避免每次命令执行都需要进行一次网络通信。
Jedis jedis = new Jedis("localhost");
Pipeline pipeline = jedis.pipelined();
pipeline.set("key1", "value1");
pipeline.set("key2", "value2");
pipeline.sync();
在上述代码中,两个set命令会一次性发送到Redis服务器,然后sync方法会一次性接收两个命令的结果。这样可以避免每次命令执行都需要进行一次网络通信,减少了网络开销。
示例:
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.JedisPoolConfig;
import redis.clients.jedis.Pipeline;
public class JedisExample {
public static void main(String[] args) {
// 创建Jedis实例,连接本地Redis
Jedis jedis = new Jedis("localhost");
// 设置键值
jedis.set("key", "value");
// 获取键值
System.out.println(jedis.get("key"));
// 使用连接池
JedisPool pool = new JedisPool(new JedisPoolConfig(), "localhost");
Jedis jedisFromPool = null;
try {
jedisFromPool = pool.getResource();
// 使用jedisFromPool进行Redis操作
jedisFromPool.set("key1", "value1");
System.out.println(jedisFromPool.get("key1"));
} finally {
if (jedisFromPool != null) {
jedisFromPool.close(); // 实际上是将连接返还给连接池,而不是关闭连接
}
}
// 使用Pipeline
Pipeline pipeline = jedis.pipelined();
pipeline.set("key2", "value2");
pipeline.set("key3", "value3");
pipeline.sync(); // 一次性发送所有命令
// 关闭Jedis
jedis.close();
}
}