Redis--Jedis应用与Spring Boot整合Redis

Redis缓存

Jedis客户端

Jedis是一个基于Java的Redis客户端连接工具,旨在提升性能与易用性。其地址是:https://github.com/redis/jedis。

创建工程

创建一个普通maven工程,然后在pom文件添加以下依赖:

  <dependency>
      <groupId>redis.clients</groupId>
      <artifactId>jedis</artifactId>
      <version>4.2.0</version>
    </dependency>
    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>3.8.1</version>
      <scope>test</scope>
    </dependency>

使用Jedis

Jedis的方法名几乎与Redis命令相同。每次使用时直接创建Jedis实例即可。

Jedis实例创建之后,其底层会创建一个指定Redis服务器的Socket连接,所以为了节省资源,每次使用完Jedis实例后,要立即调用close()方法关闭连接。

value为String

    @Test
    public void test() {
        Jedis jedis = new Jedis("192.168.220.128", 6379);
        jedis.set("name", "Alice");
        jedis.mset("age", "18","depart","IT");
        String value = jedis.get("name");
        System.out.println("Value for 'name': " + value);
        jedis.close();
    }

value为Hash

    @Test
    public void test2() {
        Jedis jedis = new Jedis("192.168.220.128", 6379);
        jedis.hset("employee", "name", "Bob");
        Map<String, String> employeeData = new HashMap<>();
        employeeData.put("age", "30");
        employeeData.put("department", "IT");
        jedis.hset("employee", employeeData);
        String name = jedis.hget("employee", "name");
        List<String> emp=jedis.hmget("employee", "department","age");
        System.out.println("Employee Name: " + name);
        System.out.println("Employee Department: " + emp.get(0));
        System.out.println("Employee Age: " + emp.get(1));
        jedis.close();
    }

value为List

 @Test
    public void test3() {
        Jedis jedis = new Jedis("192.168.220.128", 6379);
        jedis.rpush("names", "ali", "Bob", "Charlie");
        List<String> employees = jedis.lrange("names", 0, -1);
        System.out.println("Employees: " + employees);
        jedis.close();
    }

value为set

  @Test
    public void test4() {
        Jedis jedis = new Jedis("192.168.220.128", 6379);
        jedis.sadd("midWares", "Redis", "Nginx", "RocketMQ");
        Set<String> employees = jedis.smembers("midWares");
        System.out.println("Employees: " + employees);
        jedis.close();
    }

value为ZSet

 @Test
    public void test5() {
        Jedis jedis = new Jedis("192.168.220.128", 6379);
        jedis.zadd("scores", 90, "Alice");
        jedis.zadd("scores", 85, "Bob");
        jedis.zadd("scores", 95, "Charlie");
        jedis.zadd("scores", 33, "Harry");
        jedis.zadd("scores", 31, "Oliver");
        // 获取前三个员工的名字
       List<String> employees = jedis.zrevrange("scores", 0, 2);
       System.out.println("Top3: " + employees);
       List<Tuple> scores = jedis.zrevrangeWithScores("scores", 0, -1);
       for (Tuple tuple : scores) {
              System.out.println(tuple.getScore()+":"+tuple.getElement());
       }
        jedis.close();
    }

使用JedisPool

频繁的创建和销毁Jedis实例,虽然节省系统资源和网络带宽,但会降低系统性能,因为创建和销毁连接比较耗时,此时可以使用Jedis连接池来解决该问题。

JedisPool是全局性的,整个类只需要创建一次即可,然后每次操作Redis时,从JedisPool中拿出一个Redis实例即可。使用完毕无需释放Redis实例,返回给JedisPool即可。

public class JedisPoolTest {
    private JedisPool pool = new JedisPool("192.168.220.128", 6379);

    @Test
    public void test() {
        try (Jedis jedis = pool.getResource()) {
            jedis.set("name", "Alice");
            String value = jedis.get("name");
            System.out.println("Value for 'name': " + value);
        }
    }
}

使用JedisPooled

每次对Redis操作都需要使用try-with-resource块比较麻烦,使用JedisPooled 就无需使用该结构来释放资源了。

public class JedisPooledTest {
    private JedisPooled pool = new JedisPooled("192.168.220.128", 6379);

    @Test
    public void test() {
        pool.set("name", "Alice");
        String value = pool.get("name");
        System.out.println("Value for 'name': " + value);
    }
}

连接Sentinel高可用集群

直接使用JedisSentinelPool即可。

在该客户端只需注册Sentinel节点及其监控的Master的名称即可,无需出现master-slave的任何地址信息。

其采用的也是JedisPool,使用完毕需要通过close()方法将其返回给连接池。

public class JedisSentinelPoolTest {

    private JedisSentinelPool pool;
    {
        HashSet<String> sentinels = new HashSet<>();
        sentinels.add("redis:26380");
        sentinels.add("redis:26381");
        sentinels.add("redis:26382");
        pool=new JedisSentinelPool("mymaster",sentinels);
    }

    @Test
    public void test() {
            try (Jedis jedis = pool.getResource()) {
                jedis.set("name", "Alice");
                String value = jedis.get("name");
                System.out.println("Value for 'name': " + value);
            }
    }
}

连接分布式系统

使用JedisCluster即可。

底层采用的也是Jedis连接池技术,每次使用完毕无需显示关闭,其会自动关闭。

JedisCluster常用的构造器有两个:

  • 只需一个集群节点的构造器

    这个节点可以是集群中的任意节点,只要连上该节点,就连上了整个集群。

    但是这样会存在一个问题:如果连接之前这个节点恰好宕机,那么就连不上集群了。所以不推荐使用。

  • 将集群中所有节点罗列出来。这样就避免了这种风险。

public class JedisClusterTest {
    private JedisCluster jedisCluster;
    {
        HashSet<HostAndPort> nodes = new HashSet<>();
        nodes.add(new HostAndPort("127.0.0.1",6380));
        nodes.add(new HostAndPort("127.0.0.1",6381));
        nodes.add(new HostAndPort("127.0.0.1",6382));
        nodes.add(new HostAndPort("127.0.0.1",6383));
        nodes.add(new HostAndPort("127.0.0.1",6384));
        nodes.add(new HostAndPort("127.0.0.1",6385));
        nodes.add(new HostAndPort("127.0.0.1",6386));
        nodes.add(new HostAndPort("127.0.0.1",6387));
        jedisCluster = new JedisCluster(nodes);
    }

    @Test
    public void test() {
        jedisCluster.set("name", "Alice");
        String value = jedisCluster.get("name");
        System.out.println("Value for 'name': " + value);
    }
}

操作事务

Jedis提供了multi()、watch()、unwatch()来对应Redis中的multi、watch、unwatch命令。

Jedis的multi()返回一个Transaction对象,其exec()和discard()用于执行和取消事务。

抛出Java异常

public class JedisTxTest {
   private JedisPool jedisPool = new JedisPool("192.168.220.128",6379);

   @Test
    public void test() {
        try (Jedis jedis = jedisPool.getResource()) {
            jedis.set("name", "Alice");
            jedis.set("age", "30");
            Transaction tx = jedis.multi();
            try {
                    tx.set("name", "Bob");
                    int i=  3/0;
                    tx.set("age", "25");
                    tx.exec();
            } catch (Exception e) {
                System.out.println("Transaction failed: " + e.getMessage());
                tx.discard();
            } finally {
                String name = jedis.get("name");
                String age = jedis.get("age");
                System.out.println("Name: " + name);
                System.out.println("Age: " + age);
            }
        }
    }
}

输出结果是全部回滚的结果!

无标题

抛出Redis异常

   @Test
    public void test2() {
        try (Jedis jedis = jedisPool.getResource()) {
            jedis.set("name", "Alice");
            jedis.set("age", "30");
            Transaction tx = jedis.multi();
            try {
                    tx.set("name", "Bob");
                    tx.incr("name");
                    tx.set("age", "25");
                    tx.exec();
            } catch (Exception e) {
                System.out.println("Transaction failed: " + e.getMessage());
                // redis异常不会被Java代码捕获到。
                tx.discard();
            } finally {
                String name = jedis.get("name");
                String age = jedis.get("age");
                System.out.println("Name: " + name);
                System.out.println("Age: " + age);
            }
        }
    }

输出结果如下:

无标2题

说明Redis运行时异常不会被java捕获到,不影响代码的运行。

watch()

@Test
    public void test3() {
       try (Jedis jedis = jedisPool.getResource()){
            jedis.set("age", "30");
            System.out.println("加1前的值是:"+jedis.get("age"));
            jedis.watch("age");
            Transaction tx = jedis.multi();
            tx.incr("age");
            tx.exec();
            System.out.println("加1前的值是:"+jedis.get("age"));
       }
    }

SpringBoot整合Redis

Spring Boot中一般使用RedisTemplate 类来操作Redis。

  1. 创建SpringBoot项目

  2. 添加依赖

       <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-data-redis</artifactId>
       </dependency>
    
  3. 在配置文件中增加Redis相关配置

    spring:
      # 配置单机版Redis
      redis:
        host: localhost
        port: 6379
        password: 1111
    
    #  # 配置Sentinel高可用集群
    #    sentinel:
    #      master: mymaster
    #      nodes:
    #        - localhost:26380
    #        - localhost:26381
    #        - localhost:26382
    #  # 配置分布式系统
    #    cluster:
    #      nodes:
    #        - localhost:6380
    #        - localhost:6381
    #        - localhost:6382
    #        - localhost:6383
    #        - localhost:6384
    #        - localhost:6385
      # 配置缓存
      cache:
        type: redis
        cache-names: pc
    
  4. 修改实体类

    注意:要将实体类缓存到Redis,实体类必须序列化,所以要实现序列化接口 Serializable。

  5. 修改具体的实现类,主要修改写操作方法和读操作方法。

    写操作的方法如下:

      @Autowired
        private RedisTemplate<String, Object> redisTemplate;
    
        @Autowired
        private ProductDao productDao;
    
        @Transactional
        // @CacheEvict 用于实现value指定缓存空间中缓存数据的清空。allEntries为true表示清空该缓存空间中的所有数据。
        // 如果不想清空所有,可以通过key属性指定要清空的缓存数据的key。
        @CacheEvict(value = "pc", allEntries = true)
        public void saveProduct(Product product) {
            productDao.save(product);
        }
    

    读操作的方法如下:

        // @Cacheable 用于实现方法的结果进行缓存。value指定缓存空间,key指定缓存数据的key。
        // 如果缓存中存在指定key的数据,则直接返回缓存中的数据;如果不存在,则执行方法体中的代码,并将结果存入缓存中。
        @Cacheable(value = "pc", key = "'product_all'")
        public List<Product> getAllProducts() {
            return productDao.findAll();
        }
    
     // #name 表示方法参数name的值。也可以使用#root.args[0]来表示第一个参数的值。
        @Cacheable(value = "pc", key = "'product_'+#name")
        public Product getProductByName(String name) {
            return productDao.findByName(id);
        }
    
       @Autowired
        private RedisTemplate<String, Object> redisTemplate;
    
    // 获取营业额的方法,适用于方法中获取缓存的值再参与计算的场景
        public Double findTurnover(){
            // 从Redis中获取营业额数据
            BoundValueOperations<String, Object> opsForValue = redisTemplate.boundValueOps("turnover");
            // get方法的参数是要获取的缓存数据的key,这里假设营业额数据的key为"turnover"
            Double turnover = (Double) opsForValue.get();
            if (turnover == null) {
                // 如果Redis中没有数据,则从数据库中查询并存入Redis
                turnover = queryTurnoverFromDatabase();
                opsForValue.set(turnover,10, TimeUnit.MINUTES); // 将营业额数据存入Redis,设置过期时间为10分钟
            }
            return turnover;
        }
    
  6. 修改Application启动类

    @EnableCaching
    @SpringBootApplication
    public class SpringbootRedisApplication {
        public static void main(String[] args) {
            SpringApplication.run(SpringbootRedisApplication.class, args);
        }
    }
    

    @EnableCaching 用于开启当前应用的缓存功能。

总结如何将SpringBoot 与Redis整合?

  • POM中导入依赖
  • 在配置文件中注册Redis连接信息与缓存信息
  • 需要缓存的实体类必须实例化
  • 在启动类上加@EnableCaching 注解
  • 查询方法上加@Cacheable注解
  • 写操作方法上加@CacheEvict注解
  • 需要手动操作Redis方法,需要通过RedisTemplate 来获取操作对象

posted @ 2026-05-11 16:30  NE_STOP  阅读(4)  评论(0)    收藏  举报