苍穹外卖-day05

1. Redis 入门

1.1 Redis 简介:核心定位与特性

Redis(Remote Dictionary Server)是一款高性能的内存数据库,它以键值(Key-Value)结构存储数据。由于其所有数据主要存储在内存中,Redis 提供了极高的数据读写速度和吞吐量。在互联网技术领域,Redis 因其卓越的性能和灵活的数据结构支持,已成为应用最为广泛的存储中间件之一。

  • 内存数据库: 数据主要驻留在内存中,确保了快速的数据访问响应时间。
  • 键值结构: 数据以唯一的键(Key)和对应的值(Value)的形式进行存取,类似于哈希表,简单高效。
  • 存储中间件: Redis 通常作为应用程序和传统持久化数据库(如 MySQL)之间的缓冲层或辅助存储。它常用于实现高性能缓存、会话管理、消息队列、分布式锁、实时统计等多种场景。

1.2 Redis 与传统数据库的定位差异(背景知识补充)

你可能会想,既然有了像 MySQL 这样的关系型数据库,为什么还需要 Redis 呢?它们之间是互补关系,而非替代关系。

  • MySQL(关系型数据库):
    • 数据存储:主要基于磁盘,数据持久化能力强。
    • 数据结构:表格化、结构化数据,支持复杂的 SQL 查询(Join、事务等)。
    • 适用场景:适合存储大量需要永久保存的结构化数据,如核心业务数据(订单、用户信息),强调数据一致性和事务完整性。
  • Redis(非关系型数据库):
    • 数据存储:主要基于内存,读写速度极快。
    • 数据结构:提供多种丰富的非结构化数据类型(String, Hash, List, Set, Sorted Set)。
    • 适用场景:适用于高并发、低延迟的读写操作,如数据缓存、排行榜、计数器、会话管理等,旨在提升系统性能和用户体验。

在“苍穹外卖”项目中,Redis 通常会被用作缓存来加速数据访问,例如存储热门菜品、商家信息等,避免每次请求都去查询速度相对较慢的 MySQL 数据库。

2. Redis 核心数据类型介绍

Redis 存储的是 Key-Value 结构的数据,其中 Key 始终是字符串类型。而 Value 则可以支持五种常用的数据类型,每种类型都有其独特的结构和适用场景:

  • 字符串 (String): 最基本的数据类型,可以存储文本、数字或二进制数据。
  • 哈希 (Hash): 存储一个键值对集合,非常适合存储对象(如用户或商品信息)。
  • 列表 (List): 一个有序的字符串元素集合,可以从两端进行插入和删除操作,常用于实现队列或栈。
  • 集合 (Set): 一个无序的、不重复的字符串元素集合,支持集合间的交集、并集、差集等操作。
  • 有序集合 (Sorted Set / ZSet): 类似于集合,但每个成员都会关联一个 double 类型的分数,Redis 会根据分数对成员进行排序,常用于排行榜等场景。

3. Redis 常用命令

掌握 Redis 命令是直接与 Redis 服务交互的基础。以下列出并解释了各种数据类型的常用操作命令:

3.1 字符串操作命令

用于操作 String 类型的数据:

  • SET key value 设置指定 Key 的值。
  • GET key 获取指定 Key 的值。
  • SETEX key seconds value 设置指定 Key 的值,并为其设置一个过期时间(单位:秒)。
  • SETNX key value 仅当 Key 不存在时,设置 Key 的值。此命令常用于实现分布式锁。

3.2 哈希操作命令

用于操作 Hash 类型的数据,常用于存储对象:

  • HSET key field value 将哈希表 key 中字段 field 的值设为 value
  • HGET key field 获取存储在哈希表中指定字段的值。
  • HDEL key field 删除哈希表中指定字段。
  • HKEYS key 获取哈希表中所有字段。
  • HVALS key 获取哈希表中所有值。

3.3 列表操作命令

用于操作 List 类型的数据,其元素按插入顺序排序:

  • LPUSH key value1 [value2]: 将一个或多个值插入到列表头部(左侧)。
  • LRANGE key start stop 获取列表指定索引范围内的元素。
  • RPOP key 移除并获取列表的最后一个元素(右侧)。
  • LLEN key 获取列表的长度。
  • BRPOP key1 [key2] timeout 移出并获取列表的最后一个元素。如果列表为空,则阻塞直到有元素可弹出或达到超时时间。此命令在构建消息队列时非常有用。

3.4 集合操作命令

用于操作 Set 类型的数据,集合成员唯一且无序:

  • SADD key member1 [member2]: 向集合添加一个或多个成员。
  • SMEMBERS key 返回集合中的所有成员。
  • SCARD key 获取集合的成员数量。
  • SINTER key1 [key2]: 返回给定所有集合的交集。
  • SUNION key1 [key2]: 返回所有给定集合的并集。
  • SREM key member1 [member2]: 移除集合中一个或多个成员。

3.5 有序集合操作命令

用于操作 Sorted Set 类型的数据,其成员通过分数进行排序:

  • ZADD key score1 member1 [score2 member2]: 向有序集合添加一个或多个成员,并指定分数。
  • ZRANGE key start stop [WITHSCORES]: 通过索引区间返回有序集合中指定区间内的成员。WITHSCORES 可同时返回分数。
  • ZINCRBY key increment member 有序集合中对指定成员的分数增加增量 increment
  • ZREM key member [member ...]: 移除有序集合中的一个或多个成员。

3.6 通用命令

这些命令适用于所有数据类型的 Key:

  • KEYS pattern 查找所有符合给定模式 (pattern) 的 Key。警告:在生产环境中,此命令应谨慎使用。当数据量巨大时,它可能导致 Redis 服务阻塞,严重影响性能。
  • EXISTS key 检查给定 Key 是否存在。
  • TYPE key 返回 Key 所存储的值的类型(如 stringhashlistsetzset)。
  • DEL key 删除 Key。

4. 在 Java 中操作 Redis

4.1 Redis 的 Java 客户端:实现 Java 应用与 Redis 的交互

我们已经学习了 Redis 的各种命令,它们是与 Redis 服务直接交互的方式。在 Java 应用程序中,为了能够发送这些命令并接收响应,我们需要使用 Redis 的 Java 客户端库,这就像我们通过 JDBC 驱动来操作 MySQL 数据库一样。

目前主流的 Redis Java 客户端包括:

  • Jedis: 这是一个功能全面且成熟的客户端,其 API 设计与 Redis 命令高度对应,使用直接且易于理解。
  • Lettuce: 相对较新的客户端,基于 Netty 框架实现,支持异步、响应式编程模型,在高并发场景下性能表现出色。

4.2 Spring Data Redis:Spring Boot 项目的优选方案

在“苍穹外卖”这类基于 Spring Boot 的项目中,我们通常会选择使用 Spring Data Redis。Spring Data Redis 是 Spring Framework 对各种 Redis 客户端(如 Jedis 和 Lettuce)的高度抽象和集成,它提供了一套更符合 Spring 编程习惯的 API,极大地简化了 Redis 的操作。

通过引入以下 Maven 依赖,spring-boot-starter-data-redis 会自动配置 Spring Boot 项目与 Redis 的连接,并集成默认的客户端(通常是 Lettuce 或 Jedis,具体取决于 Spring Boot 版本及配置):

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

这个 Starter 依赖为我们带来了什么?

  1. 自动配置: 无需手动编写复杂的连接池、序列化器等配置,Spring Boot 会根据 application.propertiesapplication.yml 中的配置(如 spring.redis.host, spring.redis.port 等)自动创建并管理 Redis 连接工厂和 RedisTemplate 等核心组件。这在“苍穹外卖”项目中意味着你只需几行配置就能让项目具备 Redis 连接能力。
  2. Spring 风格的 API: Spring Data Redis 提供了 RedisTemplateStringRedisTemplate 等核心类,通过它们你可以用更面向对象的方式来操作 Redis 的五种数据类型,而不需要直接记忆和拼接 Redis 命令。例如,你可以直接调用 redisTemplate.opsForValue().set("key", "value") 来设置字符串,这比使用原生客户端更加便捷。
  3. 统一的异常处理: 将底层客户端的异常统一转换为 Spring 框架的 DataAccessExceptions,便于错误处理。
  4. 高度封装: 隐藏了底层客户端(Jedis/Lettuce)的具体实现细节,使得切换底层客户端变得轻而易举,无需修改业务代码。
  5. 数据序列化: 内置多种序列化器(如 Jackson2JsonRedisSerializer、JdkSerializationRedisSerializer),可以方便地将 Java 对象序列化为字节数组存储到 Redis,或从 Redis 读取后反序列化为 Java 对象。在“苍穹外卖”中,这对于缓存复杂的 Java 对象(如菜品列表、用户登录信息)至关重要。

好的,非常抱歉,看来我之前补充的细节还不够深入,没有完全满足你的期望。你提得非常对,RedisTemplate 作为 Spring Data Redis 的核心组件,是我们在 Java 代码中操作 Redis 的真正“主力”,确实需要重点讲解!

这次我一定会更详细地阐述 RedisTemplate,并结合“苍穹外卖”项目的实际应用场景进行说明。


4. 在 Java 中操作 Redis

4.1 Redis 的 Java 客户端:实现 Java 应用与 Redis 的交互

(这部分保持不变,确保衔接流畅)

我们已经学习了 Redis 的各种命令,它们是与 Redis 服务直接交互的方式。在 Java 应用程序中,为了能够发送这些命令并接收响应,我们需要使用 Redis 的 Java 客户端库,这就像我们通过 JDBC 驱动来操作 MySQL 数据库一样。

目前主流的 Redis Java 客户端包括:

  • Jedis: 这是一个功能全面且成熟的客户端,其 API 设计与 Redis 命令高度对应,使用直接且易于理解。
  • Lettuce: 相对较新的客户端,基于 Netty 框架实现,支持异步、响应式编程模型,在高并发场景下性能表现出色。

4.2 Spring Data Redis:Spring Boot 项目的优选方案

在“苍穹外卖”这类基于 Spring Boot 的项目中,我们通常会选择使用 Spring Data Redis。Spring Data Redis 是 Spring Framework 对各种 Redis 客户端(如 Jedis 和 Lettuce)的高度抽象和集成,它提供了一套更符合 Spring 编程习惯的 API,极大地简化了 Redis 的操作。

通过引入以下 Maven 依赖,spring-boot-starter-data-redis 会自动配置 Spring Boot 项目与 Redis 的连接,并集成默认的客户端(通常是 Lettuce 或 Jedis,具体取决于 Spring Boot 版本及配置):

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

这个 Starter 依赖为我们带来了什么?

  1. 自动配置: 无需手动编写复杂的连接池、序列化器等配置,Spring Boot 会根据 application.propertiesapplication.yml 中的配置(如 spring.redis.host, spring.redis.port 等)自动创建并管理 Redis 连接工厂和 RedisTemplate 等核心组件。
  2. Spring 风格的 API: 提供了一套更面向对象、更易于理解和使用的 API。
  3. 统一的异常处理: 将底层客户端的异常统一转换为 Spring 框架的 DataAccessExceptions,便于错误处理。
  4. 高度封装: 隐藏了底层客户端(Jedis/Lettuce)的具体实现细节,使得切换底层客户端变得轻而易举。
  5. 数据序列化: 内置多种序列化器,用于在 Java 对象和 Redis 存储格式之间进行转换。

4.3 深入理解 RedisTemplate:操作 Redis 的“瑞士军刀”

现在,我们来重点聊聊 RedisTemplate。它是 Spring Data Redis 提供的核心工具类,就如同 JDBC 中的 JdbcTemplate 一样,它为我们提供了一套高度抽象的、方便易用的接口来操作 Redis 的各种数据类型和命令。

RedisTemplate 的核心作用:

  • 封装底层操作: 你无需直接调用 Jedis 或 Lettuce 的客户端 API,RedisTemplate 帮你封装了底层的连接管理、命令执行等细节。
  • 类型安全操作: 它提供了对 Redis 五种核心数据类型(String, Hash, List, Set, ZSet)的专属操作接口,让你能以更符合 Java 习惯的方式来操作数据。
  • 数据序列化/反序列化: 这是 RedisTemplate 最重要的特性之一。由于 Redis 存储的是字节序列,而我们在 Java 中操作的是对象,RedisTemplate 会自动处理 Java 对象与 Redis 存储格式之间的序列化和反序列化,极大地简化了数据存取过程。
4.3.1 RedisTemplate 的常用操作接口(Ops)

RedisTemplate 本身并不直接提供 setget 等方法,而是通过一系列 opsForXxx() 方法获取对应数据类型的操作接口(Operations Interface)。这样设计,使得每种数据类型的操作都非常清晰和专业。

  1. opsForValue():操作 String(字符串)类型数据

    • 作用: 用于对 Redis 中的 String 类型 Key-Value 进行操作,包括设置、获取、递增/递减等。
    • “苍穹外卖”应用场景:
      • 缓存简单的配置信息: 比如某个开关状态、每日特价的商品 ID 等。
      • 存储验证码: redisTemplate.opsForValue().set("sms_code:" + phone, code, 5, TimeUnit.MINUTES); (设置带过期时间的验证码)
      • 缓存热门菜品的基本信息(如果菜品对象比较简单,或只缓存ID): redisTemplate.opsForValue().set("dish:1001:name", "招牌烤鸭");
  2. opsForHash():操作 Hash(哈希)类型数据

    • 作用: 用于对 Redis 中的 Hash 类型数据进行操作,每个 Hash Key 内部可以存储多个 Field-Value 对。
    • “苍穹外卖”应用场景:
      • 缓存用户信息: redisTemplate.opsForHash().put("user:100", "name", "张三"); redisTemplate.opsForHash().put("user:100", "age", 25);
      • 缓存菜品或套餐的详细信息: 将一个 DishSetmeal 对象的所有属性(如名称、价格、图片、描述等)作为 Field-Value 存储在一个 Hash Key 下,方便一次性获取或部分更新。
        // 假设有一个 Dish 对象 dish
        // redisTemplate.opsForHash().putAll("dish:" + dish.getId(), BeanUtil.beanToMap(dish)); // 使用hutool工具类将bean转为map
        
  3. opsForList():操作 List(列表)类型数据

    • 作用: 用于对 Redis 中的 List 类型数据进行操作,实现队列、栈等功能。
    • “苍穹外卖”应用场景:
      • 消息队列: 用户下单后,将订单 ID 放入一个 List 作为消息队列,后台消费者从 List 中取出处理。
      • 最新动态列表: 存储用户最近浏览的商品、最新发布的评论等。
  4. opsForSet():操作 Set(集合)类型数据

    • 作用: 用于对 Redis 中的 Set 类型数据进行操作,集合元素唯一且无序。
    • “苍穹外卖”应用场景:
      • 用户标签: redisTemplate.opsForSet().add("user:101:tags", "吃货", "夜宵党");
      • 抽奖活动参与者: 记录所有参与抽奖的用户 ID,利用 Set 的唯一性避免重复参与。
      • 共同好友/关注: 计算两个用户共同关注的商家或菜品(交集)。
  5. opsForZSet():操作 Sorted Set(有序集合)类型数据

    • 作用: 用于对 Redis 中的 Sorted Set 类型数据进行操作,元素唯一且带分数,按分数排序。
    • “苍穹外卖”应用场景:
      • 菜品/商家排行榜: 根据销售额、用户评分等作为分数,实现热门菜品榜、受欢迎商家榜。
      • 用户积分榜: 记录用户的积分,并实时更新和排序。
4.3.2 关键:数据序列化策略

在使用 RedisTemplate 缓存 Java 对象时,序列化是理解和配置的重中之重。因为 Redis 内部只存储字节序列,而 RedisTemplate 会自动处理 Java 对象与这些字节序列的转换。

Spring Data Redis 提供了多种序列化器,你需要根据实际需求选择:

  1. JdkSerializationRedisSerializer (默认配置时可能使用):

    • 特点: 使用 Java 对象的默认序列化机制。被序列化的对象需要实现 Serializable 接口。
    • 优点: 使用简单,对 Java 对象支持度好。
    • 缺点: 序列化后的数据可读性差(二进制乱码),占用空间相对较大,存在跨语言兼容性问题。在“苍穹外卖”项目中,如果直接在 Redis 中查看缓存数据,会很难识别。
  2. StringRedisSerializer

    • 特点: 专门用于 Key 和 Value 都是字符串的情况。
    • 优点: 可读性好,性能高。
    • 缺点: 只能处理 String 类型。
  3. Jackson2JsonRedisSerializerGenericJackson2JsonRedisSerializer

    • 特点: 将 Java 对象序列化为 JSON 字符串。
    • 优点: 可读性强(JSON 格式),跨语言兼容性好,占用空间相对较小。
    • 缺点: 需要引入 Jackson 库。
    • 在“苍穹外卖”中的重要性: 强烈推荐使用!因为“苍穹外卖”通常需要缓存复杂的 Java 对象(如 CategoryDishSetmealVO),将它们序列化为 JSON 存储在 Redis 中,不仅方便调试(直接看 Redis 里是 JSON 字符串),也方便前端或其他服务获取数据。

配置示例(application.yml 或 Java 配置类):

为了让 RedisTemplate 使用 JSON 序列化器,你通常需要在 Spring Boot 的配置类中进行如下配置(或通过 application.yml 配置):

@Configuration
public class RedisConfiguration {

    @Bean
    public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory connectionFactory) {
        RedisTemplate<Object, Object> redisTemplate = new RedisTemplate<>();
        redisTemplate.setConnectionFactory(connectionFactory);

        // 使用 StringRedisSerializer 来序列化和反序列化 redis 的 key 值
        redisTemplate.setKeySerializer(new StringRedisSerializer());
        redisTemplate.setHashKeySerializer(new StringRedisSerializer());

        // 使用 Jackson2JsonRedisSerializer 来序列化和反序列化 redis 的 value 值
        Jackson2JsonRedisSerializer<Object> jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer<>(Object.class);
        ObjectMapper om = new ObjectMapper();
        // 指定要序列化的域,field, get 和 set, 以及修饰符范围,ANY 是都有包括 private 和 public
        om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
        // 指定序列化输入的类型,类必须是非 final 修饰的,final 类在反序列化时会报错
        om.activateDefaultTyping(LaissezFaireSubTypeValidator.instance, ObjectMapper.DefaultTyping.NON_FINAL);
        jackson2JsonRedisSerializer.setObjectMapper(om);

        redisTemplate.setValueSerializer(jackson2JsonRedisSerializer);
        redisTemplate.setHashValueSerializer(jackson2JsonRedisSerializer); // 如果Hash的Value也是对象
        redisTemplate.afterPropertiesSet();
        return redisTemplate;
    }
}

通过上述配置,你的 RedisTemplate 在存取 Java 对象时,就会自动将其转换为可读的 JSON 格式。

4.3.3 RedisTemplate 在“苍穹外卖”项目中的实际应用

在“苍穹外卖”项目中,RedisTemplate 将无处不在,尤其是在需要提升性能和用户体验的场景:

  1. 缓存热门分类/菜品:

    • 当用户访问首页或菜品列表时,频繁查询数据库会造成压力。
    • 可以将菜品分类(Category)或热门菜品(Dish)等数据缓存到 Redis 中。
    • 例如:在 CategoryServiceDishService 中注入 RedisTemplate,首次查询数据库后,将数据存储到 Redis,后续请求直接从 Redis 获取。
    @Service
    public class CategoryServiceImpl implements CategoryService {
    
        @Autowired
        private CategoryMapper categoryMapper;
        @Autowired
        private RedisTemplate redisTemplate; // 自动注入配置好的 RedisTemplate
    
        @Override
        public List<Category> list(Integer type) {
            // 查询 Redis 缓存
            String key = "category_list:" + type;
            List<Category> categoryList = (List<Category>) redisTemplate.opsForValue().get(key);
    
            if (categoryList != null && !categoryList.isEmpty()) {
                return categoryList; // 缓存命中,直接返回
            }
    
            // 缓存未命中,查询数据库
            categoryList = categoryMapper.list(type);
    
            // 将数据存入 Redis 缓存(使用 JSON 序列化)
            if (categoryList != null && !categoryList.isEmpty()) {
                redisTemplate.opsForValue().set(key, categoryList, 1, TimeUnit.HOURS); // 缓存1小时
            }
            return categoryList;
        }
    }
    
  2. 管理用户登录 Token/Session:

    • 用户登录成功后,生成的 JWT Token 或会话信息可以存储在 Redis 中,并设置过期时间。
    • 每次请求校验 Token 时,直接从 Redis 获取,避免访问数据库。
  3. 统计访问量/点赞数:

    • 使用 Redis 的原子操作(如 incr)来快速更新菜品或商家的点赞数、浏览量。
    • 使用 Sorted Set 来维护菜品或商家的销售排行榜。
  4. 短信验证码存储:

    • 将用户手机号和验证码作为 Key-Value 存储,并设置短过期时间(如 5 分钟),方便校验。

理解 RedisTemplate 的这些操作接口和序列化机制,将使你在“苍穹外卖”项目中能够高效、灵活地利用 Redis 来提升系统性能和用户体验。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值