黑马程序员Redis教程学习笔记(第1部分):多级缓存与JVM进程缓存

2026-04-02
2
-
- 分钟
|

黑马程序员Redis教程学习笔记(第1部分):多级缓存与JVM进程缓存

课程概述

本篇笔记涵盖黑马程序员Redis教程中关于多级缓存的几个重要章节,从多级缓存的概念入手,逐步深入到JVM进程缓存的实现,再到Lua语法的基础知识。这是Redis高级应用的重要组成部分。

1. 多级缓存概念与架构

传统缓存的问题

在传统的缓存架构中,用户请求需要经过Tomcat服务器处理,Tomcat再查询Redis缓存,如果未命中才访问数据库。这种架构存在两个主要问题:

  1. 瓶颈问题:Tomcat的并发能力不如Redis,成为整个系统的瓶颈
  2. 冲击问题:当Redis缓存失效或挂掉时,所有请求直接打到数据库,对数据库造成巨大冲击

多级缓存架构

多级缓存充分利用请求处理的每一个环节,通过以下层级实现缓存: 1. 浏览器客户端缓存:静态资源缓存在本地 2. NGX本地缓存:在NGX服务器上实现缓存 3. Redis缓存:分布式缓存 4. Tomcat进程缓存:JVM进程内的缓存

这种架构的优势在于: - 大多数请求由NGX处理,减轻Tomcat压力 - 当Redis缓存失效时,还有Tomcat进程缓存作为缓冲

2. JVM进程缓存 - Caffeine

分布式缓存 vs 进程缓存

  • 分布式缓存(Redis)
    • 优点:可在集群间共享,存储容量大,可靠性高
    • 缺点:需要网络请求,存在网络延迟
  • 进程缓存(Caffeine)
    • 优点:直接读取本地内存,速度快
    • 缺点:无法在多台服务器间共享,容量受限于JVM内存,服务重启数据丢失

Caffeine介绍

Caffeine是一个基于Java 8开发的高性能本地缓存库,Spring内部的缓存很多使用的就是Caffeine。它提供了近乎最佳命中率的本地缓存解决方案。

基本用法

// 创建缓存对象
Cache<String, String> cache = Caffeine.newBuilder()
    .build();

// 存入数据
cache.put("key", "value");

// 获取数据(如果不存在返回null)
String value = cache.getIfPresent("key");

// 获取数据(如果不存在则从数据库加载并存入缓存)
String result = cache.get("key", key -> {
    // 模拟从数据库查询
    return queryFromDatabase(key);
});

缓存驱逐策略

  1. 基于容量驱逐:设置缓存上限,超过后采用LRU策略清理
  2. 基于时间驱逐:设置过期时间,超过时间后自动清理
  3. 基于引用驱逐:利用Java GC机制回收数据(不推荐使用)

实现商品查询的JVM进程缓存

缓存配置

@Configuration
public class CacheConfig {
    
    @Bean
    public Cache<Long, Item> itemCache() {
        return Caffeine.newBuilder()
            .initialCapacity(100)
            .maximumSize(10_000)
            .build();
    }
    
    @Bean
    public Cache<Long, ItemStock> stockCache() {
        return Caffeine.newBuilder()
            .initialCapacity(100)
            .maximumSize(10_000)
            .build();
    }
}

在Controller中使用缓存

@RestController
@RequestMapping("/item")
public class ItemController {
    
    @Autowired
    private Cache<Long, Item> itemCache;
    
    @Autowired
    private Cache<Long, ItemStock> stockCache;
    
    // 根据ID查询商品,使用缓存
    @GetMapping("/{id}")
    public Item queryItemById(@PathVariable("id") Long id) {
        return itemCache.get(id, key -> {
            // 缓存未命中,查询数据库
            return itemService.queryItemById(key);
        });
    }
    
    // 根据ID查询库存,使用缓存
    @GetMapping("/stock/{id}")
    public ItemStock queryStockById(@PathVariable("id") Long id) {
        return stockCache.get(id, key -> {
            // 缓存未命中,查询数据库
            return itemStockService.queryStockById(key);
        });
    }
}

3. Lua语法基础

Lua简介

Lua是一种轻量级的脚本语言,底层使用C语言编写,设计目的是为了嵌入应用程序中。在我们的多级缓存架构中,NGX服务器将使用Lua编写业务逻辑。

数据类型

  • nil:代表无效值,也可代表false
  • boolean:布尔类型,true或false
  • number:数值类型(整数、小数统一为number)
  • string:字符串类型(双引号或单引号)
  • function:函数类型
  • table:表类型(类似Java中的Map,既可表示数组也可表示Map)

类型判断

使用type()函数判断变量的数据类型:

print(type("hello"))  -- 输出: string
print(type(123))      -- 输出: number
print(type(true))     -- 输出: boolean

变量声明

-- 局部变量
local str = 'hello'
local num = 123
local bool = true

-- 数组(table形式)
local arr = {'hello', 'world', 'java'}

-- Map(key-value结构的table)
local map = {name='Jack', age=21}

访问Table元素

-- 访问数组元素(注意索引从1开始)
arr[1]  -- 'hello'

-- 访问Map元素
map['name']  -- 'Jack'
map.name     -- 'Jack'

循环遍历

-- 遍历数组
for index, value in ipairs(arr) do
    print(index, value)
end

-- 遍历Map
for key, value in pairs(map) do
    print(key, value)
end

总结

本篇笔记介绍了多级缓存的概念、JVM进程缓存的实现以及Lua语法基础。多级缓存通过在请求处理的各个环节都添加缓存,有效减轻了数据库压力并提升了系统性能。Caffeine作为JVM进程缓存的实现,提供了高效的本地缓存解决方案。而Lua语言将在NGX层实现业务逻辑,为多级缓存架构的重要组成部分。

评论交流

文章目录