第 3 课:Java 客户端操作 Redis

学习目标

完成本课后,你将掌握:

  • Java 操作 Redis 的主流方案
  • Jedis 的使用方法
  • Jedis 连接池配置
  • Spring Boot 整合 Redis
  • RedisTemplate 的使用
  • Redis 序列化问题解决方案
  • Redis 工具类封装
  • 企业项目中的 Redis 使用规范

Java 操作 Redis 的三种方案

目前 Java 项目中主流的 Redis 客户端有:

客户端 特点 推荐指数
Jedis 简单易用,传统方案 ⭐⭐⭐
Lettuce 基于 Netty,支持异步 ⭐⭐⭐⭐
Spring Data Redis Spring 官方方案 ⭐⭐⭐⭐⭐

方案对比

对比项 Jedis Lettuce Spring Data Redis
使用难度 简单 中等 简单
性能 较高
线程安全
是否推荐新项目 一般 推荐 强烈推荐
Spring Boot 整合 一般 优秀 最佳

3.1 Jedis 基本使用

什么是 Jedis?

Jedis 是 Redis 官方推荐的 Java 客户端之一。

特点:

  • API 简单
  • 学习成本低
  • 使用方便
  • 适合学习 Redis

引入依赖

Maven

<dependency>
    <groupId>redis.clients</groupId>
    <artifactId>jedis</artifactId>
    <version>4.4.3</version>
</dependency>

创建连接

import redis.clients.jedis.Jedis;

public class JedisDemo {

    public static void main(String[] args) {

        Jedis jedis = new Jedis("localhost",6379);

        System.out.println(jedis.ping());

        jedis.close();
    }

}

运行结果:

PONG

表示连接成功。


String 操作

Jedis jedis = new Jedis("localhost",6379);

jedis.set("name","zhangsan");

String name = jedis.get("name");

System.out.println(name);

jedis.close();

输出:

zhangsan

Hash 操作

Jedis jedis = new Jedis("localhost",6379);

jedis.hset("user:1001","name","lisi");

jedis.hset("user:1001","age","25");

System.out.println(
        jedis.hgetAll("user:1001")
);

jedis.close();

输出:

{name=lisi, age=25}

List 操作

Jedis jedis = new Jedis("localhost",6379);

jedis.lpush("list1","a","b","c");

System.out.println(
        jedis.lrange("list1",0,-1)
);

jedis.close();

输出:

[c,b,a]

Jedis 的问题

Jedis 对象:

不是线程安全的

错误示例:

多个线程共享同一个 Jedis

容易出现:

连接异常
数据错误
性能下降

因此:

生产环境必须使用连接池

3.1.1 Jedis 连接池

为什么需要连接池?

如果每次请求:

创建连接
↓
执行命令
↓
关闭连接

会产生大量开销。

连接池可以:

  • 复用连接
  • 提升性能
  • 降低资源消耗

配置连接池

import redis.clients.jedis.JedisPool;
import redis.clients.jedis.JedisPoolConfig;

public class JedisPoolUtil {

    private static JedisPool jedisPool;

    static {

        JedisPoolConfig config =
                new JedisPoolConfig();

        config.setMaxTotal(20);

        config.setMaxIdle(5);

        config.setMinIdle(2);

        config.setTestOnBorrow(true);

        jedisPool = new JedisPool(
                config,
                "localhost",
                6379,
                2000,
                "yourpassword"
        );

    }

    public static Jedis getJedis() {

        return jedisPool.getResource();

    }

}

获取连接

Jedis jedis = JedisPoolUtil.getJedis();

使用完成关闭

jedis.close();

注意:

这里不是销毁连接
而是归还连接池

3.2 Spring Boot 整合 Redis

企业项目中:

几乎不会直接使用 Jedis

通常使用:

Spring Data Redis

引入依赖

Spring Boot 3.x

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>
        spring-boot-starter-data-redis
    </artifactId>
</dependency>

连接池依赖

<dependency>
    <groupId>org.apache.commons</groupId>
    <artifactId>commons-pool2</artifactId>
</dependency>

application.yml 配置

spring:
  redis:
    host: localhost
    port: 6379

    password: yourpassword

    database: 0

    timeout: 2000ms

    lettuce:
      pool:
        max-active: 20
        max-idle: 5
        min-idle: 2
        max-wait: -1ms

配置说明

配置项 说明
host Redis地址
port Redis端口
password Redis密码
database 数据库编号
timeout 超时时间
max-active 最大连接数
max-idle 最大空闲连接
min-idle 最小空闲连接

RedisTemplate

Spring Boot 操作 Redis 的核心类:

RedisTemplate

相当于:

MyBatis 的 SqlSession

注入 RedisTemplate

@Resource
private RedisTemplate<String,Object> redisTemplate;

String 操作

保存数据

redisTemplate.opsForValue()
             .set("name","zhangsan");

获取数据

Object value =
        redisTemplate.opsForValue()
                     .get("name");

设置过期时间

redisTemplate.opsForValue()
        .set(
            "code",
            "123456",
            60,
            TimeUnit.SECONDS
        );

Hash 操作

设置字段

redisTemplate.opsForHash()
        .put(
                "user:1001",
                "name",
                "zhangsan"
        );

获取字段

Object name =
        redisTemplate.opsForHash()
                .get(
                    "user:1001",
                    "name"
                );

获取全部字段

Map<Object,Object> map =
        redisTemplate.opsForHash()
                .entries("user:1001");

List 操作

redisTemplate.opsForList()
        .leftPush(
                "queue",
                "message"
        );

Object msg =
        redisTemplate.opsForList()
                .rightPop("queue");

Set 操作

redisTemplate.opsForSet()
        .add(
                "tags",
                "java",
                "redis"
        );

ZSet 操作

redisTemplate.opsForZSet()
        .add(
                "rank",
                "zhangsan",
                100
        );

RedisTemplate 序列化问题

默认情况下:

RedisTemplate

使用:

JDK序列化

Redis 中会出现乱码:

\xac\xed\x00\x05...

不方便查看。


自定义 JSON 序列化

RedisConfig

@Configuration
public class RedisConfig {

    @Bean
    public RedisTemplate<String,Object>
            redisTemplate(
            RedisConnectionFactory factory
    ){

        RedisTemplate<String,Object> template =
                new RedisTemplate<>();

        template.setConnectionFactory(factory);

        StringRedisSerializer stringSerializer =
                new StringRedisSerializer();

        Jackson2JsonRedisSerializer<Object>
                jsonSerializer =
                new Jackson2JsonRedisSerializer<>(
                        Object.class
                );

        template.setKeySerializer(
                stringSerializer
        );

        template.setHashKeySerializer(
                stringSerializer
        );

        template.setValueSerializer(
                jsonSerializer
        );

        template.setHashValueSerializer(
                jsonSerializer
        );

        return template;
    }

}

企业项目 Redis 工具类封装

为了避免:

redisTemplate.opsForValue()

redisTemplate.opsForHash()

redisTemplate.opsForList()

到处出现。

一般封装:

RedisUtil

RedisUtil 示例

@Component
public class RedisUtil {

    @Resource
    private RedisTemplate<String,Object>
            redisTemplate;

    public void set(
            String key,
            Object value
    ){
        redisTemplate.opsForValue()
                .set(key,value);
    }

    public Object get(String key){
        return redisTemplate.opsForValue()
                .get(key);
    }

    public Boolean delete(String key){
        return redisTemplate.delete(key);
    }

}

企业项目实战:用户缓存

场景

用户信息查询频繁。

如果每次:

用户
 ↓
MySQL

数据库压力非常大。


正确方案

用户
 ↓
Redis
 ↓
MySQL

查询流程

查询用户
     ↓
Redis是否存在
     ↓
   是
     ↓
直接返回
     ↓
   否
     ↓
查询MySQL
     ↓
写入Redis
     ↓
返回数据

代码示例

public User getUser(Long id){

    String key = "user:" + id;

    User user =
            (User) redisUtil.get(key);

    if(user != null){

        return user;

    }

    user = userMapper.selectById(id);

    if(user != null){

        redisUtil.set(
                key,
                user
        );

    }

    return user;

}

企业项目实战:文章阅读量

Redis 计数器

文章访问:

redisTemplate.opsForValue()
        .increment(
                "article:view:1001"
        );

Redis:

article:view:1001 = 123456

优势:

  • 高性能
  • 原子操作
  • 支持高并发

本课总结

本课学习了:

  • Jedis
  • Jedis 连接池
  • Spring Boot 整合 Redis
  • RedisTemplate
  • Redis 序列化
  • Redis 工具类封装
  • 用户缓存案例
  • 阅读量统计案例

面试高频题

RedisTemplate 和 StringRedisTemplate 的区别?

类型 Value类型
RedisTemplate Object
StringRedisTemplate String

为什么 RedisTemplate 会乱码?

因为默认:

JDK序列化

需要改为:

Jackson JSON序列化

为什么生产环境不用 Jedis?

因为:

Jedis非线程安全

通常使用:

Lettuce

或者:

Spring Data Redis

课后练习

练习 1

创建 Spring Boot 项目。

整合 Redis。


练习 2

实现用户缓存功能:

查Redis
↓
查MySQL
↓
回写Redis

练习 3

实现文章阅读量统计:

increment()

练习 4

封装自己的 RedisUtil 工具类。

支持:

  • String
  • Hash
  • List
  • Set
  • ZSet

五种数据结构。


下一课预告

第 4 课:Redis 持久化机制

学习内容:

  • RDB
  • AOF
  • 混合持久化
  • 数据恢复
  • 持久化策略选择
  • 企业生产环境配置