杰瑞科技汇

Redis Java教程怎么学?入门到实践步骤有哪些?

Redis Java 教程:从入门到实战

本教程将教你如何使用 Java 语言与 Redis 进行交互,涵盖连接、基本操作、数据类型、连接池、Spring Boot 集成以及一些高级用法。

Redis Java教程怎么学?入门到实践步骤有哪些?-图1
(图片来源网络,侵删)

目录

  1. 准备工作
    • 安装并启动 Redis
    • 配置 Java 开发环境
    • 添加 Redis Java 客户端依赖
  2. 建立与 Redis 的连接
    • 使用 Jedis
    • 使用 Lettuce (推荐)
  3. Redis 基本数据类型操作
    • String (字符串)
    • Hash (哈希)
    • List (列表)
    • Set (集合)
    • Sorted Set (有序集合)
  4. 其他常用命令
    • Key (键) 操作
    • TTL (生存时间)
  5. 高级特性
    • 管道
    • 事务
    • 发布/订阅
  6. 连接池配置 (最佳实践)
    • Jedis 连接池
    • Lettuce 连接池
  7. 在 Spring Boot 中使用 Redis
    • 添加依赖
    • 配置 application.properties
    • 使用 @Cacheable 注解实现缓存
    • 使用 RedisTemplate 进行编程式操作
  8. 总结与最佳实践

准备工作

1 安装并启动 Redis

如果你还没有安装 Redis,请先完成安装。

  • Linux/macOS: 大多通过包管理器安装,如 sudo apt-get install redis-server
  • Windows: 可以使用 WSL (Windows Subsystem for Linux) 或下载预编译的二进制文件。

安装完成后,启动 Redis 服务器:

redis-server

为了方便测试,你可以打开另一个终端,启动 Redis 客户端:

redis-cli

2 配置 Java 开发环境

确保你已经安装了 JDK (建议 8 或以上版本) 和 Maven 或 Gradle。

Redis Java教程怎么学?入门到实践步骤有哪些?-图2
(图片来源网络,侵删)

3 添加 Redis Java 客户端依赖

目前最主流的 Redis Java 客户端有两个:JedisLettuce

  • Jedis: 一个轻量级、直接的网络通信库,API 比较直接,但连接管理需要自己处理(通常用连接池)。
  • Lettuce: 基于 Netty 的、更现代化的客户端,支持同步、异步和响应式编程,内置了连接池管理,是目前更推荐的选择。

这里我们以 Maven 为例,添加 Lettuce 依赖,如果你使用 Gradle,build.gradle 文件中的配置类似。

Maven (pom.xml):

<dependencies>
    <!-- Lettuce 客户端 -->
    <dependency>
        <groupId>io.lettuce</groupId>
        <artifactId>lettuce-core</artifactId>
        <version>6.2.6.RELEASE</version> <!-- 建议使用最新稳定版 -->
    </dependency>
    <!-- 为了方便测试,我们添加 JUnit -->
    <dependency>
        <groupId>org.junit.jupiter</groupId>
        <artifactId>junit-jupiter-api</artifactId>
        <version>5.9.2</version>
        <scope>test</scope>
    </dependency>
</dependencies>

建立与 Redis 的连接

我们将重点介绍 Lettuce 的用法,因为它更现代且功能强大。

1 使用 Lettuce

Lettuce 使用 RedisClientRedisCommands 接口来操作 Redis。

import io.lettuce.core.RedisClient;
import io.lettuce.core.RedisURI;
import io.lettuce.core.api.StatefulRedisConnection;
import io.lettuce.core.api.sync.RedisCommands;
public class LettuceExample {
    public static void main(String[] args) {
        // 1. 创建 RedisURI,指定连接信息
        RedisURI redisUri = RedisURI.create("redis://localhost:6379");
        // 2. 创建 RedisClient 客户端
        RedisClient redisClient = RedisClient.create(redisUri);
        // 3. 获取一个连接
        // 注意:每次连接和关闭都有开销,实际应用中应使用连接池
        StatefulRedisConnection<String, String> connection = redisClient.connect();
        // 4. 获取同步操作的命令接口
        RedisCommands<String, String> syncCommands = connection.sync();
        // 5. 执行命令
        syncCommands.set("user:1001:name", "张三");
        String name = syncCommands.get("user:1001:name");
        System.out.println("获取到的名字是: " + name);
        // 6. 关闭连接
        connection.close();
        redisClient.shutdown();
    }
}

运行这段代码,你应该能在控制台看到 获取到的名字是: 张三


Redis 基本数据类型操作

Lettuce 的 API 设计非常直观,命令名称和 Redis 原生命名基本一致。

1 String (字符串)

字符串是 Redis 最基本的数据类型。

// 设置值,如果键已存在则覆盖
syncCommands.set("key1", "hello redis");
// 获取值
String value = syncCommands.get("key1");
System.out.println(value); // 输出: hello redis
// 追加值
syncCommands.append("key1", "!");
System.out.println(syncCommands.get("key1")); // 输出: hello redis!
// 获取字符串长度
long len = syncCommands.strlen("key1");
System.out.println(len); // 输出: 12
// 设置并获取旧值
String oldValue = syncCommands.getset("key1", "new value");
System.out.println(oldValue); // 输出: hello redis!

2 Hash (哈希)

Hash 是一个键值对集合,适合存储对象。

// 设置单个字段
syncCommands.hset("user:1001", "name", "李四");
syncCommands.hset("user:1001", "age", "25");
syncCommands.hset("user:1001", "city", "北京");
// 获取单个字段
String name = syncCommands.hget("user:1001", "name");
System.out.println(name); // 输出: 李四
// 获取所有字段和值
Map<String, String> allFields = syncCommands.hgetall("user:1001");
System.out.println(allFields);
// 输出: {name=李四, age=25, city=北京}
// 获取所有字段
List<String> fields = syncCommands.hkeys("user:1001");
System.out.println(fields); // 输出: [name, age, city]
// 获取所有值
List<String> values = syncCommands.hvals("user:1001");
System.out.println(values); // 输出: [李四, 25, 北京]
// 删除字段
syncCommands.hdel("user:1001", "city");

3 List (列表)

列表是简单的字符串列表,按插入顺序排序,可以充当栈或队列。

// 将元素推入列表左侧 (LPUSH)
syncCommands.lpush("mylist", "world", "hello");
// 获取列表范围 (0 到 -1 表示全部)
List<String> myList = syncCommands.lrange("mylist", 0, -1);
System.out.println(myList); // 输出: [hello, world] (注意顺序,因为是从左推入)
// 获取列表长度
long len = syncCommands.llen("mylist");
System.out.println(len); // 输出: 2
// 从右侧弹出元素 (RPOP)
String element = syncCommands.rpop("mylist");
System.out.println(element); // 输出: world

4 Set (集合)

集合是无序的、唯一的字符串集合。

// 添加元素 (如果元素已存在,则忽略)
syncCommands.sadd("myset", "apple", "banana", "orange", "apple");
// 获取集合中所有成员
Set<String> mySet = syncCommands.smembers("myset");
System.out.println(mySet); // 输出: [orange, banana, apple] (顺序不固定)
// 检查元素是否存在
boolean exists = syncCommands.sismember("myset", "banana");
System.out.println(exists); // 输出: true
// 获取集合大小
long size = syncCommands.scard("myset");
System.out.println(size); // 输出: 3
// 移除元素
syncCommands.srem("myset", "apple");

5 Sorted Set (有序集合)

有序集合是 Set 的升级版,每个元素都会关联一个 double 类型的分数,Redis 通过分数来为集合中的成员进行从小到大的排序。

// 添加成员及其分数
syncCommands.zadd("zset1", 100, "member1");
syncCommands.zadd("zset1", 200, "member2");
syncCommands.zadd("zset1", 50, "member3");
// 获取排名区间的成员 (从低到高)
// ZRANGE zset1 0 -1
Set<String> lowToHigh = syncCommands.zrange("zset1", 0, -1);
System.out.println(lowToHigh); // 输出: [member3, member1, member2]
// 获取排名区间的成员及其分数 (WITHSCORES)
// ZRANGE zset1 0 -1 WITHSCORES
Map<String, Double> lowToHighWithScores = syncCommands.zrangeWithScores("zset1", 0, -1);
System.out.println(lowToHighWithScores);
// 输出: {member3=50.0, member1=100.0, member2=200.0}
// 获取分数区间的成员 (ZRANGEBYSCORE)
// ZRANGEBYSCORE zset1 60 150
Set<String> byScoreRange = syncCommands.zrangebyscore("zset1", 60, 150);
System.out.println(byScoreRange); // 输出: [member1, member2]
// 获取成员的排名 (从低到高)
long rank = syncCommands.zrank("zset1", "member1");
System.out.println(rank); // 输出: 1

其他常用命令

1 Key (键) 操作

// 检查键是否存在
boolean exists = syncCommands.exists("key1");
System.out.println(exists);
// 删除键
syncCommands.del("key1");
// 设置键的过期时间 (单位: 秒)
syncCommands.setex("mykey", 10, "this will expire in 10 seconds");
// 查找所有符合模式的键 (注意:生产环境慎用 KEYS 命令,可能会阻塞服务器)
// 使用 SCAN 命令代替
Set<String> allKeys = syncCommands.keys("*");
System.out.println(allKeys);

2 TTL (生存时间)

TTL 命令用于查看一个键的剩余生存时间(以秒为单位)。

// 设置一个有过期时间的键
syncCommands.setex("temp_key", 60, "value");
// 查看剩余时间
// -2 表示键不存在
// -1 表示键存在但没有设置过期时间
long ttl = syncCommands.ttl("temp_key");
System.out.println("剩余时间: " + ttl + " 秒");
// 移除过期时间
syncCommands.persist("temp_key");
long newTtl = syncCommands.ttl("temp_key");
System.out.println("移除过期时间后的TTL: " + newTtl); // 输出: -1

高级特性

1 管道

管道可以一次性发送多个命令到 Redis 服务器,并一次性获取所有结果,减少了网络往返次数,大大提高了性能。

StatefulRedisConnection<String, String> connection = redisClient.connect();
RedisCommands<String, String> syncCommands = connection.sync();
// 使用 pipeline
RedisClusterCommands<String, String> pipeline = connection.multi(); // 对于单机版,使用 multi() 或直接使用 syncCommands 的批量操作
// 在实际应用中,Lettuce 的 Pipeline 使用方式如下:
// 你可以累积命令,然后在一个事务中执行,或者使用异步命令来提高吞吐量。
// 这里展示一个简单的批量执行方式。
List<String> keys = List.of("key1", "key2", "key3");
List<String> values = List.of("val1", "val2", "val3");
// 批量设置
for (int i = 0; i < keys.size(); i++) {
    syncCommands.set(keys.get(i), values.get(i));
}
// 批量获取
List<String> results = new ArrayList<>();
for (String key : keys) {
    results.add(syncCommands.get(key));
}
System.out.println("批量获取结果: " + results);
// 更高级的用法是使用异步命令或事务
connection.close();

2 事务

Redis 事务可以一次性执行多个命令,这些命令会被序列化、按顺序执行,并且在执行期间不会被其他客户端的命令打断。

// MULTI 标记事务开始
// EXEC 执行事务中的所有命令
// DISCARD 取消事务
syncCommands.multi(); // 发送 MULTI 命令
syncCommands.set("tx_key1", "tx_val1");
syncCommands.set("tx_key2", "tx_val2");
syncCommands.incr("tx_counter"); // 假设 tx_counter 是一个数字
// 发送 EXEC 命令,执行事务
List<Object> results = syncCommands.exec(); // 结果是一个列表,按顺序对应每个命令的返回值
System.out.println("事务执行结果: " + results);
// 输出可能类似: [OK, OK, 1] (tx_counter 原来是 0)

3 发布/订阅

发布/订阅是一种消息通信模式,发送者(发布者)发送消息,而订阅者接收消息。

发布者代码:

StatefulRedisConnection<String, String> pubConnection = redisClient.connect();
RedisCommands<String, String> pubCommands = pubConnection.sync();
System.out.println("发布者启动,正在向 'news' 频道发送消息...");
pubCommands.publish("news", "今天天气不错!");
pubCommands.publish("news", "Redis 教程更新了!");
pubCommands.publish("sports", "中国队赢了!");
pubConnection.close();

订阅者代码 (需要单独运行):

StatefulRedisConnection<String, String> subConnection = redisClient.connect();
RedisPubSubListener<String, String> listener = new RedisPubSubListener<String, String>() {
    @Override
    public void message(String channel, String message) {
        System.out.println("收到来自频道 '" + channel + "' 的消息: " + message);
    }
    @Override
    public void subscribed(String channel, long count) {
        System.out.println("订阅了频道: " + channel);
    }
    @Override
    public void unsubscribed(String channel, long count) {
        System.out.println("取消订阅了频道: " + channel);
    }
};
// 订阅一个或多个频道
subConnection.sync().subscribe(listener, "news");
// 为了保持订阅,主线程不能退出,这里让线程睡眠一会儿。
try {
    Thread.sleep(10000); // 睡眠10秒,接收消息
} catch (InterruptedException e) {
    e.printStackTrace();
}
// 取消订阅
subConnection.sync().unsubscribe("news");
subConnection.close();

连接池配置 (最佳实践)

每次操作都创建和关闭连接是非常低效的,在实际应用中,必须使用连接池

1 Lettuce 连接池

Lettuce 从 5.1 版本开始,官方推荐使用 LettuceConnectionFactory,它会自动管理连接池。

如果你想在纯 Java 环境中使用连接池,可以这样配置:

import io.lettuce.core.RedisClient;
import io.lettuce.core.RedisURI;
import io.lettuce.core.api.StatefulRedisConnection;
import io.lettuce.core.support.ConnectionPoolSupport;
import org.apache.commons.pool2.impl.GenericObjectPool;
import org.apache.commons.pool2.impl.GenericObjectPoolConfig;
public class LettucePoolExample {
    public static void main(String[] args) {
        RedisURI redisUri = RedisURI.create("redis://localhost:6379");
        RedisClient redisClient = RedisClient.create(redisUri);
        // 1. 创建连接池配置
        GenericObjectPoolConfig<StatefulRedisConnection<String, String>> poolConfig = new GenericObjectPoolConfig<>();
        poolConfig.setMaxTotal(20); // 最大连接数
        poolConfig.setMaxIdle(10);  // 最大空闲连接数
        poolConfig.setMinIdle(5);   // 最小空闲连接数
        // 2. 创建连接池
        GenericObjectPool<StatefulRedisConnection<String, String>> connectionPool =
                ConnectionPoolSupport.createGenericObjectPool(() -> redisClient.connect(), poolConfig);
        try (StatefulRedisConnection<String, String> connection = connectionPool.borrowObject()) {
            RedisCommands<String, String> commands = connection.sync();
            commands.set("pool_key", "pool_value");
            System.out.println("从连接池获取并操作: " + commands.get("pool_key"));
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            // 3. 关闭连接池和客户端
            connectionPool.close();
            redisClient.shutdown();
        }
    }
}

注意: 上面的例子使用了 Apache Commons Pool2,Spring Boot 已经为你封装好了这一切,无需手动配置。


在 Spring Boot 中使用 Redis

Spring Boot 对 Redis 的集成非常友好,是开发中最高效的方式。

1 添加依赖

pom.xml 中添加 spring-boot-starter-data-redis,它会自动为你配置好 Lettuce 和连接池。

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

2 配置 application.properties

src/main/resources/application.properties 中配置 Redis 连接信息。

# Redis服务器地址
spring.redis.host=localhost
# Redis服务器连接端口
spring.redis.port=6379
# Redis服务器连接密码(默认为空)
# spring.redis.password=
# 连接池配置
spring.redis lettuce.pool.max-active=20
spring.redis lettuce.pool.max-idle=10
spring.redis lettuce.pool.min-idle=5

3 使用 @Cacheable 注解实现缓存

Spring Cache 提供了基于注解的缓存功能,非常方便。

  1. 在主启动类上添加 @EnableCaching

    import org.springframework.boot.SpringApplication;
    import org.springframework.boot.autoconfigure.SpringBootApplication;
    import org.springframework.cache.annotation.EnableCaching;
    @SpringBootApplication
    @EnableCaching // 启用缓存
    public class MyApplication {
        public static void main(String[] args) {
            SpringApplication.run(MyApplication.class, args);
        }
    }
  2. 创建一个 Service

    import org.springframework.cache.annotation.Cacheable;
    import org.springframework.stereotype.Service;
    @Service
    public class UserService {
        // 模拟从数据库查询
        public User getUserFromDB(String userId) {
            System.out.println("正在从数据库查询用户: " + userId);
            // 模拟耗时操作
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            if ("1001".equals(userId)) {
                return new User("1001", "张三");
            }
            return null;
        }
        // @Cacheable 注解会拦截方法调用
        // 如果缓存中有,则直接返回缓存结果,不执行方法体
        // 如果缓存中没有,则执行方法体,并将返回值存入缓存
        // cacheNames 指定缓存的名称,key 指定缓存的键
        @Cacheable(cacheNames = "users", key = "#userId")
        public User getUser(String userId) {
            return getUserFromDB(userId);
        }
    }
    class User {
        private String id;
        private String name;
        // ... 构造方法, getter, setter
        public User(String id, String name) {
            this.id = id;
            this.name = name;
        }
        @Override
        public String toString() {
            return "User{" + "id='" + id + '\'' + ", name='" + name + '\'' + '}';
        }
    }
  3. 测试

    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.boot.CommandLineRunner;
    import org.springframework.stereotype.Component;
    @Component
    public class MyRunner implements CommandLineRunner {
        @Autowired
        private UserService userService;
        @Override
        public void run(String... args) throws Exception {
            System.out.println("--- 第一次调用,会执行方法体 ---");
            User user1 = userService.getUser("1001");
            System.out.println("获取到用户: " + user1);
            System.out.println("\n--- 第二次调用,会从缓存中读取,不会执行方法体 ---");
            User user2 = userService.getUser("1001");
            System.out.println("获取到用户: " + user2);
        }
    }

运行程序,你会发现第一次调用会打印 "正在从数据库查询用户...",而第二次则不会,说明缓存生效了。

4 使用 RedisTemplate 进行编程式操作

当你需要更精细的控制时,可以直接注入 RedisTemplate

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import java.util.List;
import java.util.Map;
import java.util.Set;
@Service
public class RedisTemplateService {
    @Autowired
    private RedisTemplate<String, Object> redisTemplate; // Spring Boot 默认配置的 RedisTemplate
    public void operate() {
        // 操作 String
        redisTemplate.opsForValue().set("template:msg", "Hello RedisTemplate");
        System.out.println(redisTemplate.opsForValue().get("template:msg"));
        // 操作 Hash
        redisTemplate.opsForHash().put("template:user:1001", "name", "王五");
        redisTemplate.opsForHash().put("template:user:1001", "age", 30);
        Map<Object, Object> user = redisTemplate.opsForHash().entries("template:user:1001");
        System.out.println(user);
        // 操作 List
        redisTemplate.opsForList().leftPushAll("template:list", "a", "b", "c");
        List<Object> list = redisTemplate.opsForList().range("template:list", 0, -1);
        System.out.println(list);
        // 操作 Set
        redisTemplate.opsForSet().add("template:set", "apple", "banana", "orange");
        Set<Object> set = redisTemplate.opsForSet().members("template:set");
        System.out.println(set);
    }
}

总结与最佳实践

  1. 选择合适的客户端:对于新项目,优先选择 Lettuce,它功能更现代,支持异步和响应式编程。
  2. 使用连接池:无论使用哪个客户端,在生产环境中都必须使用连接池,避免频繁创建和销毁连接带来的性能开销,Spring Boot 已经为你做好了配置。
  3. 为 Key 设计合理的命名规范:例如使用 分隔,如 user:1001:name,这能让 Redis 的内存结构更清晰,也方便通过 KEYS 命令(或更安全的 SCAN)进行管理。
  4. 注意数据类型的选择:根据业务场景选择最合适的数据类型,例如用 Hash 存储对象,用 List 实现队列,用 Sorted Set 实现排行榜。
  5. 合理设置 TTL:为缓存数据设置合理的过期时间,防止数据长期占用内存。
  6. 慎用 KEYS 命令KEYS * 会阻塞 Redis 服务器,在生产环境中应使用 SCAN 命令进行迭代式的查找。
  7. 利用 Spring Cache:在 Spring 生态中,优先使用 @Cacheable 等注解,可以极大地简化缓存逻辑,让代码更专注于业务。

希望这份详细的教程能帮助你顺利地在 Java 项目中使用 Redis!

分享:
扫描分享到社交APP
上一篇
下一篇