java redis(超详细)

更新时间:

💡一则或许对你有用的小广告

欢迎加入小哈的星球 ,你将获得:专属的项目实战(已更新的所有项目都能学习) / 1v1 提问 / Java 学习路线 / 学习打卡 / 每月赠书 / 社群讨论

  • 新开坑项目:《Spring AI 项目实战》 正在持续爆肝中,基于 Spring AI + Spring Boot 3.x + JDK 21..., 点击查看 ;
  • 《从零手撸:仿小红书(微服务架构)》 已完结,基于 Spring Cloud Alibaba + Spring Boot 3.x + JDK 17...点击查看项目介绍 ;演示链接: http://116.62.199.48:7070 ;
  • 《从零手撸:前后端分离博客项目(全栈开发)》 2 期已完结,演示链接: http://116.62.199.48/ ;

截止目前, 星球 内专栏累计输出 90w+ 字,讲解图 3441+ 张,还在持续爆肝中.. 后续还会上新更多项目,目标是将 Java 领域典型的项目都整一波,如秒杀系统, 在线商城, IM 即时通讯,权限管理,Spring Cloud Alibaba 微服务等等,已有 3100+ 小伙伴加入学习 ,欢迎点击围观

在现代互联网应用开发中,Java Redis 的组合已成为高性能后端架构的标配。Redis 以其快速的内存存储能力和丰富的数据结构特性,为 Java 应用提供了高效的缓存、会话管理、消息队列等解决方案。无论是初学者构建第一个缓存系统,还是中级开发者优化复杂业务场景,掌握 Java Redis 的核心原理与实战技巧都至关重要。本文将从零开始,通过循序渐进的讲解和实际案例,帮助读者系统性地理解这一技术栈。


一、Redis 基础概念解析

1.1 什么是 Redis?

Redis 是一个基于内存的高性能键值对数据库,支持多种数据类型(如字符串、列表、哈希等),并提供了丰富的原子操作和持久化能力。它的核心优势在于:

  • 高速读写:内存操作使 Redis 的响应时间通常在毫秒级。
  • 数据结构丰富:支持复杂的数据模型,能直接操作集合、队列等结构,减少业务逻辑复杂度。
  • 持久化与高可用:通过 RDB 快照和 AOF 日志实现数据持久化,结合主从复制和集群模式保障高可用性。

形象比喻
可以把 Redis 想象成一个超大型图书馆,每个书架(键)对应一本特定的书籍(值),而书籍的类型可以是小说(字符串)、百科全书(哈希)、杂志合订本(列表)等。读者(客户端)可以通过书架编号快速找到书籍,甚至直接操作书籍内容(如删除某页或添加新章节)。

1.2 Redis 与 Java 的结合场景

在 Java 开发中,Redis 常用于以下场景:

  • 缓存加速:将高频访问的数据(如商品详情、用户信息)存入 Redis,减少数据库压力。
  • 分布式锁:利用 Redis 的原子性操作实现跨节点的锁机制,避免竞态条件。
  • 消息队列:通过 List 或 Stream 数据结构构建轻量级队列系统。
  • 实时统计:用 HyperLogLog 统计独立访客,或用有序集合实现排行榜。

二、Java 与 Redis 的集成实践

2.1 选择合适的 Java 客户端

主流的 Java Redis 客户端有 JedisLettuce

  • Jedis 是老牌客户端,API 简单易用,适合中小型项目。
  • Lettuce 支持 Netty 异步通信,性能更高,适合高并发场景。

2.1.1 使用 Jedis 连接 Redis

import redis.clients.jedis.Jedis;  

public class RedisExample {  
    public static void main(String[] args) {  
        // 连接本地 Redis 服务器  
        Jedis jedis = new Jedis("localhost", 6379);  
        // 设置键值  
        jedis.set("username", "Alice");  
        // 获取并打印值  
        String value = jedis.get("username");  
        System.out.println("Username: " + value);  
        // 关闭连接  
        jedis.close();  
    }  
}  

2.1.2 使用 Lettuce 连接 Redis

import io.lettuce.core.RedisClient;  
import io.lettuce.core.api.StatefulRedisConnection;  

public class LettuceExample {  
    public static void main(String[] args) {  
        RedisClient redisClient = RedisClient.create("redis://localhost:6379");  
        try (StatefulRedisConnection<String, String> connection = redisClient.connect()) {  
            connection.sync().set("age", "25");  
            String age = connection.sync().get("age");  
            System.out.println("Age: " + age);  
        } finally {  
            redisClient.shutdown();  
        }  
    }  
}  

三、Redis 核心数据类型与操作

3.1 字符串(String)

字符串是 Redis 最基础的数据类型,支持单值存储,可配合过期时间实现临时缓存。

// 设置键 "counter" 的值为 100,并设置 300 秒后过期  
jedis.setex("counter", 300, "100");  

// 原子递增操作  
long newValue = jedis.incr("counter");  // 结果为 101  

3.2 列表(List)

列表支持在两端快速插入和弹出元素,常用于队列或栈场景。

// 将 "task1" 添加到列表 "queue" 的头部  
jedis.lpush("queue", "task1");  

// 从列表尾部弹出元素  
String task = jedis.rpop("queue");  

3.3 哈希(Hash)

哈希适合存储对象属性,例如用户信息:

// 存储用户信息  
jedis.hset("user:1001", "name", "Bob");  
jedis.hset("user:1001", "email", "bob@example.com");  

// 获取单个字段值  
String email = jedis.hget("user:1001", "email");  

3.4 集合(Set)与有序集合(Sorted Set)

  • Set 存储无序不重复的元素,适合去重操作。
  • Sorted Set 通过分数(score)排序,常用于排行榜。
// 向集合 "tags" 中添加元素  
jedis.sadd("tags", "java", "redis");  

// 向有序集合 "leaderboard" 添加用户 "userA" 得分 150  
jedis.zadd("leaderboard", 150.0, "userA");  

四、进阶应用与性能优化

4.1 缓存策略与一致性问题

4.1.1 缓存更新策略

常见的策略包括:

  • 缓存穿透:使用布隆过滤器或“空值缓存”防止无效键查询。
  • 缓存击穿:热点键过期时引发的瞬时高并发,可通过设置随机过期时间或互斥锁解决。
// 使用互斥锁防止缓存击穿  
public String getCacheWithMutex(String key) {  
    if (jedis.exists(key)) {  
        return jedis.get(key);  
    }  
    // 尝试获取锁  
    if (jedis.setnx("lock:" + key, "locked") == 1) {  
        try {  
            // 从数据库查询并设置缓存  
            String value = queryFromDB(key);  
            jedis.setex(key, 3600, value);  
            return value;  
        } finally {  
            jedis.del("lock:" + key);  
        }  
    } else {  
        // 等待其他线程设置缓存完成  
        return getCacheWithMutex(key);  
    }  
}  

4.1.2 缓存与数据库双写一致性

可通过以下方式解决:

  • 最终一致性:允许短暂不一致,通过后台任务修复。
  • 数据库驱动更新:将数据库更新操作与 Redis 缓存操作封装为一个事务。

4.2 性能优化技巧

4.2.1 连接池配置

使用连接池避免频繁创建连接,示例如下:

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

JedisPoolConfig config = new JedisPoolConfig();  
config.setMaxTotal(100);  
config.setMaxIdle(10);  
JedisPool pool = new JedisPool(config, "localhost", 6379);  

// 获取连接并操作  
try (Jedis jedis = pool.getResource()) {  
    jedis.set("key", "value");  
}  

4.2.2 管道(Pipeline)批量操作

通过管道减少网络延迟,提升批量操作性能:

try (Pipeline pipelined = jedis.pipelined()) {  
    pipelined.set("key1", "value1");  
    pipelined.set("key2", "value2");  
    pipelined.sync();  // 执行所有命令  
}  

五、实战案例:电商秒杀库存优化

5.1 场景描述

在电商秒杀场景中,商品库存需严格保证“不超卖”。传统数据库更新方式在高并发下可能因延迟导致超卖。

5.2 Redis 解决方案

利用 Redis 的原子操作和 Lua 脚本保证库存操作的原子性:

// Lua 脚本:原子减少库存  
String script = "local current = tonumber(redis.call('get', KEYS[1])) " +  
                "if current and current > 0 then " +  
                "  redis.call('decr', KEYS[1]) " +  
                "  return current - 1 " +  
                "else " +  
                "  return 0 " +  
                "end";  

// 执行脚本  
Long result = (Long) jedis.eval(script, Collections.singletonList("stock:1001"), 0);  
if (result > 0) {  
    // 扣减成功,执行下单逻辑  
} else {  
    // 库存不足  
}  

结论

通过本文,读者已掌握 Java Redis 的核心概念、集成方法、数据操作技巧以及进阶优化策略。从基础的键值存储到复杂的分布式锁、秒杀场景,Redis 展现出其在 Java 后端开发中的强大能力。建议读者在实际项目中逐步实践,结合具体业务需求选择合适的策略,并持续关注 Redis 的新特性(如 Redis Modules)。掌握这一技术栈,将为构建高性能、可扩展的系统奠定坚实基础。


本文内容聚焦于 Java Redis 的核心知识,通过案例与代码示例帮助开发者快速上手。如需深入探讨特定场景或优化方案,可进一步查阅官方文档或相关技术社区资源。

最新发布