查询数据库是耗时操作,如果同一接口被频繁调用,每次都查数据库会严重影响性能。引入 Redis 缓存后,数据先从缓存中取,缓存没有再去查数据库,响应速度能提升 10~100 倍。

一、为什么需要缓存

场景 无缓存 有 Redis 缓存
热点数据反复查询 每次都查数据库 第一次查数据库,之后走缓存
接口响应时间 300~500ms 5~30ms
数据库压力

二、准备工作

1. 安装 Redis(Windows)

下载 Redis for Windows 并启动即可,默认端口 6379。

2. 引入依赖

<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>

3. 配置 Redis

spring:
  redis:
    host: localhost
    port: 6379
    timeout: 3000ms
    lettuce:
      pool:
        max-active: 8
        max-idle: 8
        min-idle: 0

三、手动操作 Redis(最灵活的方式)

使用 RedisTemplate 手动读写缓存:

@Service
public class UserService {

    @Autowired
    private RedisTemplate<String, Object> redisTemplate;

    private static final String USER_CACHE_KEY = "user:";

    public User getUserById(Long id) {
        String key = USER_CACHE_KEY + id;

        // 1. 先从缓存中取
        User user = (User) redisTemplate.opsForValue().get(key);
        if (user != null) {
            System.out.println("从缓存中获取用户: " + id);
            return user;
        }

        // 2. 缓存没有,查数据库
        user = userMapper.selectById(id);
        if (user != null) {
            // 3. 放入缓存,过期时间 30 分钟
            redisTemplate.opsForValue().set(key, user, 30, TimeUnit.MINUTES);
            System.out.println("从数据库查询并缓存用户: " + id);
        }

        return user;
    }

    public void updateUser(User user) {
        userMapper.updateById(user);
        // 更新后删除缓存,下次查询重新加载
        redisTemplate.delete(USER_CACHE_KEY + user.getId());
    }
}

RedisTemplate 配置

@Configuration
public class RedisConfig {

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

        // 使用 Jackson 序列化(存储为 JSON)
        Jackson2JsonRedisSerializer<Object> jackson = new Jackson2JsonRedisSerializer<>(Object.class);
        ObjectMapper mapper = new ObjectMapper();
        mapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
        mapper.activateDefaultTyping(LazyValidatorFactory.ObjectMapperDefaultTyping.NON_FINAL);
        jackson.setObjectMapper(mapper);

        // key 用 String 序列化,value 用 JSON 序列化
        template.setKeySerializer(new StringRedisSerializer());
        template.setValueSerializer(jackson);
        template.setHashKeySerializer(new StringRedisSerializer());
        template.setHashValueSerializer(jackson);

        return template;
    }
}

四、用注解方式(最推荐)

Spring 提供了声明式缓存注解,一行注解搞定缓存

@Service
public class UserService {

    @Cacheable(value = "user", key = "#id")
    public User getUserById(Long id) {
        System.out.println("查询数据库: " + id);
        return userMapper.selectById(id);
    }

    @CachePut(value = "user", key = "#user.id")
    public User updateUser(User user) {
        userMapper.updateById(user);
        return user;
    }

    @CacheEvict(value = "user", key = "#id")
    public void deleteUser(Long id) {
        userMapper.deleteById(id);
    }
}

常用注解说明

注解 作用 适用场景
@Cacheable 先查缓存,有则返回,无则执行方法并缓存结果 查询接口
@CachePut 执行方法,并将结果更新到缓存 更新接口
@CacheEvict 执行方法,并删除缓存 删除接口

启用缓存注解

@SpringBootApplication
@EnableCaching  // 开启缓存注解
public class Application {
    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }
}

五、防止缓存雪崩和穿透

1. 设置合理的过期时间

// 缓存过期时间加上随机值,防止同时过期(缓存雪崩)
long expireTime = 30 + new Random().nextInt(10);  // 30~40分钟
redisTemplate.opsForValue().set(key, value, expireTime, TimeUnit.MINUTES);

2. 防止缓存穿透(查询不存在的数据)

public User getUserById(Long id) {
    String key = "user:" + id;

    // 缓存中有特殊标记,直接返回 null
    if (redisTemplate.hasKey(key)) {
        Object val = redisTemplate.opsForValue().get(key);
        if (val == null) {
            return null;  // 之前查过数据库,不存在
        }
        return (User) val;
    }

    User user = userMapper.selectById(id);
    if (user == null) {
        // 数据不存在,也缓存一个空值,防止穿透
        redisTemplate.opsForValue().set(key, null, 5, TimeUnit.MINUTES);
    } else {
        redisTemplate.opsForValue().set(key, user, 30, TimeUnit.MINUTES);
    }

    return user;
}

六、Redis 可视化工具推荐

平时开发建议装一个可视化工具,方便查看缓存数据:

  • Another Redis Desktop Manager(推荐,免费)
  • Redis Insight(官方出品)

总结

Redis 缓存是 Java 后端开发的核心技能。记住三条原则:

  1. 读多写少的数据才加缓存(比如用户信息、配置)
  2. 设置合理的过期时间(避免缓存雪崩)
  3. 缓存空值(防止缓存穿透)

如果对你有帮助,欢迎点赞、评论、关注【张老师技术栈】,持续分享 Java/Python/爬虫 实战干货。

Logo

汇聚全球AI编程工具,助力开发者即刻编程。

更多推荐