当前位置:   article > 正文

【Redis】Java操作Redis(Jedis客户端使用)_java 连接redis

java 连接redis

Redis不仅支持简单的键值存储,还提供了丰富的数据结构(如列表、哈希表、集合等)和强大的原子操作,使得它在存储和处理数据时非常高效。关于这些数据结构的学习可以学习下面的博客:

【Redis】String的常用命令及图解String使用场景_string命令在做什么-CSDN博客

【Redis】哈希类型的常用命令以及使用场景-CSDN博客

【Redis】List的常用命令以及常用场景-CSDN博客

【Redis】Set 集合常用命令以及使用场景-CSDN博客

【Redis】Zset有序集合常用命令以及使用场景-CSDN博客

1. 引言

在实际的开发中,我们总不可能直接还是在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实例时,它们会共享这些全局属性,这可能导致以下问题:

  1. 数据混乱和不一致性: 多个线程同时操作Redis的写流和读流,可能导致数据的交叉写入或读取,从而产生不可预测的结果。
  2. 并发操作异常: 在高并发情况下,多个线程对同一个Jedis实例的并发操作可能触发Jedis内部状态的不一致,进而导致异常或数据丢失。

解决方案:使用Jedis连接池(JedisPool)

为了避免上述问题,推荐的做法是为每个线程分配独立的Jedis实例,而不是共享一个实例。这可以通过Jedis连接池(JedisPool)来实现,连接池能够有效地管理多个Jedis实例,并确保线程安全的使用方式。

2. 快速上手

2.1 配置相关以及环境要求

在maven仓库找到Jedis的依赖:

  1. <dependencies>
  2. <!-- https://mvnrepository.com/artifact/redis.clients/jedis -->
  3. <dependency>
  4. <groupId>redis.clients</groupId>
  5. <artifactId>jedis</artifactId>
  6. <version>4.4.2</version>
  7. </dependency>
  8. </dependencies>

在导入依赖后,并不是直接写代码就可以上手的。目前一般来说使用的环境是在windows上,而现在我的Redis客户端是在服务器上的。直接开放redis的端口是比较危险的,因此这里一般有两个做法:

  1. 直接让java程序也在服务器上运行。也就是把自己写好的代码打成jar包拷贝到linux的服务器上执行。手动还挺麻烦的,也有一些第三方的插件来简化这些步骤;
  2. 配置ssh端口转发,把云服务器的redis端口映射到本地主机。

这里主要讲解一下通过配置端口转发来解决上述问题。

此时访问本地端口就相当于访问远程服务器的端口。

在 xshell 中,进行如下配置:
1)右键云服务器的会话,选择属性

2)找到隧道 ->配置转移规则

3) 使⽤该会话连接服务器.
此时,访问本地的 8888,就相当于访问对应服务器的 6379。注意,xshell和服务器必须处在连接状态,这样的映射才是有效的。
此外,还要注意安装redis的配置文件设置,可以去看博客: Redis介绍及安装配置_credis安装-CSDN博客的第三节。

 2.2 连接Redis Server

  1. public class RedisExample {
  2. public static void main(String[] args) {
  3. // 创建Jedis对象,指定Redis服务器地址和端口号
  4. Jedis jedis = new Jedis("127.0.0.1", 8888);
  5. // 如果Redis服务器设置了密码,需要进行认证
  6. // jedis.auth("password");
  7. // 测试连接是否正常
  8. System.out.println("连接成功");
  9. // 存储数据
  10. jedis.set("name", "Alice");
  11. String value = jedis.get("name");
  12. System.out.println("Stored value in Redis: " + value);
  13. // 关闭连接
  14. jedis.close();
  15. }

3.3 Jedis针对不同数据结构

Redis支持多种数据结构,例如哈希表、列表、集合等,可以使用Jedis进行操作:

  1. / 连接Redis
  2. Jedis jedis = new Jedis("localhost", 6379);
  3. // 使用哈希表存储用户信息
  4. jedis.hset("user:1", "name", "Alice");
  5. jedis.hset("user:1", "age", "30");
  6. // 获取用户信息
  7. String name = jedis.hget("user:1", "name");
  8. String age = jedis.hget("user:1", "age");
  9. System.out.println("User name: " + name + ", age: " + age);
  10. // 关闭连接
  11. jedis.close();

jedis实例实现了大多数Redis命令。有关支持的命令的完整列表,请参阅jedis Javadocs:Jedis - jedis 5.2.0-beta4 javadocicon-default.png?t=N7T8https://www.javadoc.io/doc/redis.clients/jedis/latest/redis/clients/jedis/Jedis.html 

3.4 连接池管理

在Java应用中使用Jedis操作Redis时,使用连接池(如JedisPool)是非常重要的。

Jedis实例是线程不安全的。Jedis实例中的RedisOutputStreamRedisInputStream是全局属性,当多个线程同时使用同一个Jedis实例时,会导致数据混乱和不可预测的结果。因此,需要保证每个线程都使用独立的Jedis实例。

  1. import redis.clients.jedis.Jedis;
  2. import redis.clients.jedis.JedisPool;
  3. import redis.clients.jedis.JedisPoolConfig;
  4. public class RedisExample {
  5. private static JedisPool jedisPool;
  6. static {
  7. // 配置连接池
  8. JedisPoolConfig poolConfig = new JedisPoolConfig();
  9. poolConfig.setMaxTotal(10); // 设置最大连接数
  10. poolConfig.setMaxIdle(5); // 设置最大空闲连接数
  11. poolConfig.setMinIdle(1); // 设置最小空闲连接数
  12. poolConfig.setTestOnBorrow(true); // 借用连接时进行测试
  13. // 初始化连接池
  14. jedisPool = new JedisPool(poolConfig, "localhost", 8888, 2000, "your_redis_password"); // 如果有密码,请填写
  15. }
  16. public static void main(String[] args) {
  17. // 使用连接池获取Jedis实例
  18. try (Jedis jedis = jedisPool.getResource()) {
  19. // 测试连接是否正常
  20. System.out.println("连接成功: " + jedis.ping());
  21. // 进行Redis操作
  22. jedis.set("key", "value");
  23. String value = jedis.get("key");
  24. System.out.println("Stored value in Redis: " + value);
  25. } catch (Exception e) {
  26. e.printStackTrace();
  27. } finally {
  28. // 关闭连接池
  29. if (jedisPool != null) {
  30. jedisPool.close();
  31. }
  32. }
  33. }
  34. }

连接池配置解释

  • setMaxTotal: 最大连接数,连接池中能够分配的最大Jedis实例数。
  • setMaxIdle: 最大空闲连接数,连接池中允许保留的最大空闲Jedis实例数。
  • setMinIdle: 最小空闲连接数,连接池中最小的空闲Jedis实例数,低于这个数量时,连接池会创建新的实例。
  • setTestOnBorrow: 借用连接时进行测试,确保获取的Jedis实例可用。

对于线程池还不了解到可以去看这篇博客:

深入解剖线程池(ThreadPoolExecutor)_newfixedthreadpool 线程释放-CSDN博客
线程池中的参数很相似。

连接池参数源码

  1. public class RedisPoolUtils {
  2. private static JedisPool jedisPool = null;
  3. /**
  4. * redis服务器地址
  5. */
  6. private static String addr = "127.0.0.1";
  7. /**
  8. * redis服务器端口
  9. */
  10. private static int port = 6379;
  11. /**
  12. * redis服务器密码
  13. */
  14. private static String auth = "111111";
  15. static {
  16. try {
  17. JedisPoolConfig config = new JedisPoolConfig();
  18. // 连接耗尽时是否阻塞, false报异常,ture阻塞直到超时, 默认true
  19. config.setBlockWhenExhausted(true);
  20. // 设置的逐出策略类名, 默认DefaultEvictionPolicy(当连接超过最大空闲时间,或连接数超过最大空闲连接数)
  21. config.setEvictionPolicyClassName("org.apache.commons.pool2.impl.DefaultEvictionPolicy");
  22. // 是否启用pool的jmx管理功能, 默认true
  23. config.setJmxEnabled(true);
  24. // MBean ObjectName = new ObjectName("org.apache.commons.pool2:type=GenericObjectPool,name=" + "pool" + i); 默认为"pool", JMX不熟,具体不知道是干啥的...默认就好.
  25. config.setJmxNamePrefix("pool");
  26. // 是否启用后进先出, 默认true
  27. config.setLifo(true);
  28. // 最大空闲连接数, 默认8个
  29. config.setMaxIdle(8);
  30. // 最大连接数, 默认8个
  31. config.setMaxTotal(8);
  32. // 获取连接时的最大等待毫秒数(如果设置为阻塞时BlockWhenExhausted),如果超时就抛异常, 小于零:阻塞不确定的时间, 默认-1
  33. config.setMaxWaitMillis(-1);
  34. // 逐出连接的最小空闲时间 默认1800000毫秒(30分钟)
  35. config.setMinEvictableIdleTimeMillis(1800000);
  36. // 最小空闲连接数, 默认0
  37. config.setMinIdle(0);
  38. // 每次逐出检查时 逐出的最大数目 如果为负数就是 : 1/abs(n), 默认3
  39. config.setNumTestsPerEvictionRun(3);
  40. // 对象空闲多久后逐出, 当空闲时间>该值 且 空闲连接>最大空闲数 时直接逐出,不再根据MinEvictableIdleTimeMillis判断 (默认逐出策略)
  41. config.setSoftMinEvictableIdleTimeMillis(1800000);
  42. // 在获取连接的时候检查有效性, 默认false
  43. config.setTestOnBorrow(false);
  44. // 在空闲时检查有效性, 默认false
  45. config.setTestWhileIdle(false);
  46. // 逐出扫描的时间间隔(毫秒) 如果为负数,则不运行逐出线程, 默认-1
  47. config.setTimeBetweenEvictionRunsMillis(-1);
  48. jedisPool = new JedisPool(config, addr, port, 3000, auth);
  49. } catch (Exception e) {
  50. e.printStackTrace();
  51. }
  52. }
  53. /**
  54. * 获取 Jedis 资源
  55. *
  56. * @return
  57. */
  58. public static Jedis getJedis() {
  59. if (jedisPool != null) {
  60. return jedisPool.getResource();
  61. }
  62. return null;
  63. }
  64. /**
  65. * 释放Jedis资源
  66. */
  67. public static void close(final Jedis jedis) {
  68. if (jedis != null) {
  69. jedis.close();
  70. }
  71. }
  72. }

 

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/从前慢现在也慢/article/detail/948865
推荐阅读
相关标签
  

闽ICP备14008679号