赞
踩
Redis不仅支持简单的键值存储,还提供了丰富的数据结构(如列表、哈希表、集合等)和强大的原子操作,使得它在存储和处理数据时非常高效。关于这些数据结构的学习可以学习下面的博客:
【Redis】String的常用命令及图解String使用场景_string命令在做什么-CSDN博客
【Redis】Set 集合常用命令以及使用场景-CSDN博客
【Redis】Zset有序集合常用命令以及使用场景-CSDN博客
在实际的开发中,我们总不可能直接还是在redis-cli中使用命令来操作吧。就像是MySQL我们可以通过JDBC\MyBatis等来操作数据库,在java中同样可以使用java客户端的Jedis来操作redis。使用Redis客户端,比如Jedis,可以方便地在Java应用程序中与Redis进行通信,从而利用其快速的数据读写能力和丰富的数据操作功能。
Jedis是Redis官方推荐的Java客户端之一,它基于Redis的RESP(Redis Serialization Protocol)协议实现,提供了简洁清晰的API,支持连接池管理和管道操作,同时在社区中有广泛的使用和支持。
在使用Jedis作为Java应用与Redis交互的客户端时,一个常见的问题是其线程安全性。Jedis是直连模式的客户端,意味着在多线程环境下共享一个Jedis实例可能会导致数据混乱和不一致性。
edis的线程不安全性主要源于其底层的RedisOutputStream和RedisInputStream对象,它们是Jedis中的全局属性。当多个线程同时使用同一个Jedis实例时,它们会共享这些全局属性,这可能导致以下问题:
解决方案:使用Jedis连接池(JedisPool)
为了避免上述问题,推荐的做法是为每个线程分配独立的Jedis实例,而不是共享一个实例。这可以通过Jedis连接池(JedisPool)来实现,连接池能够有效地管理多个Jedis实例,并确保线程安全的使用方式。
在maven仓库找到Jedis的依赖:
- <dependencies>
-
- <!-- https://mvnrepository.com/artifact/redis.clients/jedis -->
- <dependency>
- <groupId>redis.clients</groupId>
- <artifactId>jedis</artifactId>
- <version>4.4.2</version>
- </dependency>
-
- </dependencies>
在导入依赖后,并不是直接写代码就可以上手的。目前一般来说使用的环境是在windows上,而现在我的Redis客户端是在服务器上的。直接开放redis的端口是比较危险的,因此这里一般有两个做法:
这里主要讲解一下通过配置端口转发来解决上述问题。
此时访问本地端口就相当于访问远程服务器的端口。
在 xshell 中,进行如下配置:
1)右键云服务器的会话,选择属性
2)找到隧道 ->配置转移规则
- public class RedisExample {
- public static void main(String[] args) {
- // 创建Jedis对象,指定Redis服务器地址和端口号
- Jedis jedis = new Jedis("127.0.0.1", 8888);
-
- // 如果Redis服务器设置了密码,需要进行认证
- // jedis.auth("password");
-
- // 测试连接是否正常
- System.out.println("连接成功");
-
-
- // 存储数据
- jedis.set("name", "Alice");
- String value = jedis.get("name");
- System.out.println("Stored value in Redis: " + value);
-
- // 关闭连接
- jedis.close();
- }
Redis支持多种数据结构,例如哈希表、列表、集合等,可以使用Jedis进行操作:
- / 连接Redis
- Jedis jedis = new Jedis("localhost", 6379);
-
- // 使用哈希表存储用户信息
- jedis.hset("user:1", "name", "Alice");
- jedis.hset("user:1", "age", "30");
-
- // 获取用户信息
- String name = jedis.hget("user:1", "name");
- String age = jedis.hget("user:1", "age");
- System.out.println("User name: " + name + ", age: " + age);
-
- // 关闭连接
- jedis.close();
jedis实例实现了大多数Redis命令。有关支持的命令的完整列表,请参阅jedis Javadocs:Jedis - jedis 5.2.0-beta4 javadochttps://www.javadoc.io/doc/redis.clients/jedis/latest/redis/clients/jedis/Jedis.html
在Java应用中使用Jedis操作Redis时,使用连接池(如JedisPool)是非常重要的。
Jedis实例是线程不安全的。Jedis实例中的RedisOutputStream
和RedisInputStream
是全局属性,当多个线程同时使用同一个Jedis实例时,会导致数据混乱和不可预测的结果。因此,需要保证每个线程都使用独立的Jedis实例。
- import redis.clients.jedis.Jedis;
- import redis.clients.jedis.JedisPool;
- import redis.clients.jedis.JedisPoolConfig;
-
- public class RedisExample {
- private static JedisPool jedisPool;
-
- static {
- // 配置连接池
- JedisPoolConfig poolConfig = new JedisPoolConfig();
- poolConfig.setMaxTotal(10); // 设置最大连接数
- poolConfig.setMaxIdle(5); // 设置最大空闲连接数
- poolConfig.setMinIdle(1); // 设置最小空闲连接数
- poolConfig.setTestOnBorrow(true); // 借用连接时进行测试
-
- // 初始化连接池
- jedisPool = new JedisPool(poolConfig, "localhost", 8888, 2000, "your_redis_password"); // 如果有密码,请填写
- }
-
- public static void main(String[] args) {
- // 使用连接池获取Jedis实例
- try (Jedis jedis = jedisPool.getResource()) {
- // 测试连接是否正常
- System.out.println("连接成功: " + jedis.ping());
-
- // 进行Redis操作
- jedis.set("key", "value");
- String value = jedis.get("key");
- System.out.println("Stored value in Redis: " + value);
- } catch (Exception e) {
- e.printStackTrace();
- } finally {
- // 关闭连接池
- if (jedisPool != null) {
- jedisPool.close();
- }
- }
- }
- }
setMaxTotal
: 最大连接数,连接池中能够分配的最大Jedis实例数。setMaxIdle
: 最大空闲连接数,连接池中允许保留的最大空闲Jedis实例数。setMinIdle
: 最小空闲连接数,连接池中最小的空闲Jedis实例数,低于这个数量时,连接池会创建新的实例。setTestOnBorrow
: 借用连接时进行测试,确保获取的Jedis实例可用。对于线程池还不了解到可以去看这篇博客:
深入解剖线程池(ThreadPoolExecutor)_newfixedthreadpool 线程释放-CSDN博客
线程池中的参数很相似。
- public class RedisPoolUtils {
- private static JedisPool jedisPool = null;
-
- /**
- * redis服务器地址
- */
- private static String addr = "127.0.0.1";
-
- /**
- * redis服务器端口
- */
- private static int port = 6379;
-
- /**
- * redis服务器密码
- */
- private static String auth = "111111";
-
-
- static {
- try {
- JedisPoolConfig config = new JedisPoolConfig();
- // 连接耗尽时是否阻塞, false报异常,ture阻塞直到超时, 默认true
- config.setBlockWhenExhausted(true);
- // 设置的逐出策略类名, 默认DefaultEvictionPolicy(当连接超过最大空闲时间,或连接数超过最大空闲连接数)
- config.setEvictionPolicyClassName("org.apache.commons.pool2.impl.DefaultEvictionPolicy");
- // 是否启用pool的jmx管理功能, 默认true
- config.setJmxEnabled(true);
- // MBean ObjectName = new ObjectName("org.apache.commons.pool2:type=GenericObjectPool,name=" + "pool" + i); 默认为"pool", JMX不熟,具体不知道是干啥的...默认就好.
- config.setJmxNamePrefix("pool");
- // 是否启用后进先出, 默认true
- config.setLifo(true);
- // 最大空闲连接数, 默认8个
- config.setMaxIdle(8);
- // 最大连接数, 默认8个
- config.setMaxTotal(8);
- // 获取连接时的最大等待毫秒数(如果设置为阻塞时BlockWhenExhausted),如果超时就抛异常, 小于零:阻塞不确定的时间, 默认-1
- config.setMaxWaitMillis(-1);
- // 逐出连接的最小空闲时间 默认1800000毫秒(30分钟)
- config.setMinEvictableIdleTimeMillis(1800000);
- // 最小空闲连接数, 默认0
- config.setMinIdle(0);
- // 每次逐出检查时 逐出的最大数目 如果为负数就是 : 1/abs(n), 默认3
- config.setNumTestsPerEvictionRun(3);
- // 对象空闲多久后逐出, 当空闲时间>该值 且 空闲连接>最大空闲数 时直接逐出,不再根据MinEvictableIdleTimeMillis判断 (默认逐出策略)
- config.setSoftMinEvictableIdleTimeMillis(1800000);
- // 在获取连接的时候检查有效性, 默认false
- config.setTestOnBorrow(false);
- // 在空闲时检查有效性, 默认false
- config.setTestWhileIdle(false);
- // 逐出扫描的时间间隔(毫秒) 如果为负数,则不运行逐出线程, 默认-1
- config.setTimeBetweenEvictionRunsMillis(-1);
- jedisPool = new JedisPool(config, addr, port, 3000, auth);
- } catch (Exception e) {
- e.printStackTrace();
- }
- }
-
- /**
- * 获取 Jedis 资源
- *
- * @return
- */
- public static Jedis getJedis() {
- if (jedisPool != null) {
- return jedisPool.getResource();
- }
- return null;
- }
-
- /**
- * 释放Jedis资源
- */
- public static void close(final Jedis jedis) {
- if (jedis != null) {
- jedis.close();
- }
- }
- }
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。