上一篇我们完成了Redis五大核心数据结构的收官学习,借助ZSet实现了排行榜、热度排序等动态排序场景,掌握了List、Hash、Set、ZSet各自的业务适配逻辑。而Redis最核心、最普及的用途,依旧是数据库查询缓存——面对高并发查询、慢SQL场景,缓存能大幅降低数据库压力,把接口响应从百毫秒压缩至毫秒级。

很多新手刚接触Redis缓存,容易陷入“存错数据、清不掉缓存、数据不一致”的误区。本篇聚焦最实用的入门场景:缓存数据库查询结果,从缓存原理、流程设计、代码实战到注意事项,手把手带你落地稳定的缓存逻辑,实现性能与稳定性的双赢。

核心定位:利用Redis内存高速读写的特性,缓存数据库中查询频繁、改动较少的结果,拦截大部分重复查询请求,缓解数据库IO压力,提升系统响应速度。

一、为什么要用Redis缓存数据库查询?

在传统Web应用中,用户查询数据的流程是:请求→应用服务→数据库查询→返回结果。当并发量升高或存在慢SQL时,数据库很容易成为性能瓶颈,而Redis缓存恰好能解决这些痛点:

  • 性能差距悬殊:MySQL磁盘查询耗时百毫秒级,Redis内存查询耗时亚毫秒级,速度提升100倍以上
  • 降低数据库负载:重复查询直接走缓存,减少数据库连接和查询次数,避免数据库宕机
  • 提升用户体验:接口响应更快,减少页面加载等待时间,降低卡顿概率
  • 适配高并发场景:支撑秒杀、首页访问、热点数据查询等高并发流量冲击

缓存前提:优先缓存查询频繁、修改较少、实时性要求不高的数据,比如文章详情、用户基础信息、商品基础信息、字典配置等;实时性极强的数据(如库存、余额)不建议盲目缓存。


二、缓存核心模式:旁路缓存(Cache Aside)

企业级开发中,缓存数据库查询99%都采用旁路缓存模式,逻辑简单、稳定性高、易落地,分为读缓存写缓存两个流程,是新手入门的首选方案。

1. 读数据流程(查询缓存)

  1. 接收查询请求,先拼接规范的Redis Key
  2. 查询Redis缓存,若缓存存在,直接返回结果(缓存命中)
  3. 若缓存不存在(缓存未命中),查询数据库获取结果
  4. 将数据库结果存入Redis,并设置合理过期时间TTL
  5. 返回查询结果给用户

2. 写数据流程(更新/删除)

  1. 先更新/删除数据库中的数据(保证数据库数据最新)
  2. 立即删除对应的Redis缓存(不建议直接更新缓存,避免数据不一致)
  3. 下次查询时,重新从数据库加载最新数据并缓存

三、实战步骤:缓存数据库查询结果

第一步:制定缓存Key规范

缓存Key是缓存的唯一标识,必须规范命名,避免冲突、便于维护,严禁用简单数字或无意义字符串作为Key。

通用命名格式业务模块:数据类型:唯一标识

  • 用户信息缓存:user:info:1001(1001为用户ID)
  • 文章详情缓存:article:detail:2024(2024为文章ID)
  • 商品信息缓存:goods:info:6688(6688为商品ID)

第二步:读缓存代码实战(SpringBoot+RedisTemplate)

查询文章详情为例,演示完整的缓存查询逻辑,适配MySQL+Redis架构,新手可直接复用。

/**
 * 文章服务:查询文章详情(带Redis缓存)
 */
@Service
public class ArticleService {

    @Resource
    private ArticleMapper articleMapper;

    @Resource
    private RedisTemplate<String, Object> redisTemplate;

    // 缓存Key前缀
    private static final String ARTICLE_CACHE_KEY = "article:detail:";
    // 缓存过期时间:2小时(承接前文TTL知识)
    private static final long CACHE_EXPIRE = 7200;

    /**
     * 根据ID查询文章详情(缓存优先)
     * @param articleId 文章ID
     * @return 文章详情
     */
    public Article getArticleById(Long articleId) {
        // 1. 拼接完整缓存Key
        String cacheKey = ARTICLE_CACHE_KEY + articleId;

        // 2. 先查询Redis缓存
        Article cacheArticle = (Article) redisTemplate.opsForValue().get(cacheKey);
        // 缓存命中,直接返回
        if (cacheArticle != null) {
            return cacheArticle;
        }

        // 3. 缓存未命中,查询数据库
        Article dbArticle = articleMapper.selectById(articleId);
        // 数据库无数据,直接返回(避免缓存穿透,后文详解)
        if (dbArticle == null) {
            return null;
        }

        // 4. 将数据库结果存入Redis,设置过期时间
        redisTemplate.opsForValue().set(cacheKey, dbArticle, CACHE_EXPIRE, TimeUnit.SECONDS);

        // 5. 返回结果
        return dbArticle;
    }
}

第三步:写缓存(更新/删除)代码实战

数据更新后,必须清理缓存,否则会出现缓存数据与数据库不一致的问题,严格遵循“先更新库,后删缓存”原则。

/**
 * 更新文章信息(清理缓存)
 */
public boolean updateArticle(Article article) {
    // 1. 先更新数据库,保证数据最新
    int rows = articleMapper.updateById(article);
    if (rows <= 0) {
        return false;
    }

    // 2. 立即删除对应的Redis缓存
    String cacheKey = ARTICLE_CACHE_KEY + article.getArticleId();
    redisTemplate.delete(cacheKey);

    return true;
}

/**
 * 删除文章(清理缓存)
 */
public boolean deleteArticle(Long articleId) {
    // 1. 先删除数据库数据
    int rows = articleMapper.deleteById(articleId);
    if (rows <= 0) {
        return false;
    }

    // 2. 删除缓存
    String cacheKey = ARTICLE_CACHE_KEY + articleId;
    redisTemplate.delete(cacheKey);

    return true;
}

四、缓存数据库查询的核心注意事项

1. 必须设置缓存过期时间(TTL)

严禁缓存永久有效,否则冷数据会持续占用Redis内存,导致内存溢出。根据数据变动频率设置TTL:

  • 静态数据(文章、配置):1~24小时
  • 热点数据(首页、热门商品):5~30分钟
  • 临时数据:1~5分钟

2. 规避缓存数据不一致

  • 严格执行:先更新数据库,再删除缓存,禁止先删缓存再更库(避免并发查询脏数据)
  • 不建议直接更新缓存,更新逻辑复杂且容易出错
  • 核心数据可加分布式锁,避免并发修改导致缓存错乱

3. 解决缓存穿透问题

缓存穿透指查询数据库不存在的数据,请求会直接打到数据库,恶意请求会压垮数据库。

入门解决方案:缓存空值,即使数据库无数据,也将空结果存入Redis,设置短过期时间。

// 优化后:处理缓存穿透
if (dbArticle == null) {
    // 缓存空值,过期时间设为1分钟
    redisTemplate.opsForValue().set(cacheKey, null, 60, TimeUnit.SECONDS);
    return null;
}

4. 避免缓存雪崩

缓存雪崩指大量缓存同一时间过期,所有请求瞬间打到数据库。

入门解决方案:给过期时间加随机偏移量,打散批量过期时间。

// 基础过期时间+随机300秒内偏移,避免同时过期
long randomExpire = CACHE_EXPIRE + new Random().nextInt(300);
redisTemplate.opsForValue().set(cacheKey, dbArticle, randomExpire, TimeUnit.SECONDS);

5. 慎用缓存的场景

  • 实时性要求极高的数据(如库存、账户余额、订单状态)
  • 频繁修改、写入远大于读取的数据
  • 数据量极小、查询频率极低的数据(缓存收益远低于维护成本)

五、入门实践总结

Redis缓存数据库查询,是后端性能优化的第一步,核心逻辑可浓缩为三句话:

  1. 读数据:先查缓存,缓存未命中再查数据库,结果回塞缓存并设TTL
  2. 写数据:先更数据库,再删缓存,保证数据最终一致
  3. 避坑核心:设TTL、防穿透、打散过期时间

按照本篇流程落地,既能快速实现接口性能提升,又能规避新手常见缓存问题,后续可在此基础上深入学习缓存击穿、分布式缓存等进阶知识。

最终效果:优化后,重复查询接口响应速度提升5~10倍,数据库查询次数减少80%以上,高并发场景稳定性大幅增强。

发表回复