Redis专题-实战篇二-商户查询缓存

本文涉及的产品
多模态交互后付费免费试用,全链路、全Agent
简介: 本文介绍了缓存的基本概念、应用场景及实现方式,涵盖Redis缓存设计、缓存更新策略、缓存穿透问题及其解决方案。重点讲解了缓存空对象与布隆过滤器的使用,并通过代码示例演示了商铺查询的缓存优化实践。

一、什么是缓存

  1. 缓存:是数据存的缓冲区,是存储数据的地方,一般读写性能较好
  2. 以WEB访问,缓存存在的各个地方
  1. 浏览器缓存:静态的CSS、JS脚本或图片
  2. Tomcat缓存:使用Redis对于

  1. 缓存的优缺点

二、查询商户的业务流程

三、具体业务代码实现

  1. 具体代码实现
package com.hmdp.service.impl;
import cn.hutool.Hutool;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONNull;
import cn.hutool.json.JSONUtil;
import com.hmdp.dto.Result;
import com.hmdp.entity.Shop;
import com.hmdp.mapper.ShopMapper;
import com.hmdp.service.IShopService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import io.netty.util.internal.StringUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import static com.hmdp.utils.RedisConstants.CACHE_SHOP_KEY;
/**
 * <p>
 *  服务实现类
 * </p>
 *
 * @author
 * @since 2021-12-22
 */
@Service
public class ShopServiceImpl extends ServiceImpl<ShopMapper, Shop> implements IShopService {
    @Autowired
    private StringRedisTemplate stringRedisTemplate;
    /**
     * 通过ID查询商铺的详细信息
     * @param id
     * @return
     */
    @Override
    public Result querById(Long id) {
        String redisId = CACHE_SHOP_KEY +id;
        // 1 先通过redis查询
        String s = stringRedisTemplate.opsForValue().get(redisId);
        if (StrUtil.isNotBlank(s)) {
            Shop shop = JSONUtil.toBean(s, Shop.class);
            return Result.ok(shop);
        }
        // 返回空,查询数据库
        Shop shop = getById(id);
        // 数据库返回空,返回错误信息
        if (shop==null) {
            return Result.fail("商户不存在");
        }
        // 不为空返回数据并将数据存储在redis中
        stringRedisTemplate.opsForValue().set(redisId,JSONUtil.toJsonStr(shop));
        return Result.ok(shop);
    }
}
  1. 效果对比
  1. 查询数据库

  1. 查询redis

四、练习题:商铺分类列表redis缓存改造

  1. 实现方法和上章节类似,但是采用的存储方法为Zset
  2. 实现代码
package com.hmdp.service.impl;
import cn.hutool.json.JSONUtil;
import com.hmdp.dto.Result;
import com.hmdp.entity.ShopType;
import com.hmdp.mapper.ShopTypeMapper;
import com.hmdp.service.IShopTypeService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.sun.xml.internal.bind.v2.runtime.reflect.Lister;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import java.util.*;
/**
 * <p>
 *  服务实现类
 * </p>
 *
 * @author 虎哥
 * @since 2021-12-22
 */
@Slf4j
@Service
public class ShopTypeServiceImpl extends ServiceImpl<ShopTypeMapper, ShopType> implements IShopTypeService {
    @Autowired
    StringRedisTemplate stringRedisTemplate;
    @Override
    public Result queryTypeList() {
        String key = "catch:shopType";
        Set<String> range = stringRedisTemplate.opsForZSet().range(key, 0, 10);
      List<ShopType> list= new ArrayList<>();
        if (range.size()!=0) {
            range.stream().forEach(item ->{
                ShopType bean = JSONUtil.toBean(item, ShopType.class);
                list.add(bean);
            });
            log.debug("redis get catch:shopType");
        return Result.ok(list);
        }
        List<ShopType> sort = query().orderByAsc("sort").list();
        if (sort.size()==0) {
            return  Result.fail("系统错误");
        }
        HashSet<ShopType> shopTypes = new HashSet<>();
        shopTypes.stream().forEach(item->{
            stringRedisTemplate.opsForZSet().add(key,JSONUtil.toJsonStr(item),item.getSort());
        });
        log.debug("Mysql get shopTypeList");
        return Result.ok(sort);
    }
}

五、缓存更新策略

1. 三种策略模式

2. 策略选择分析

  • 低一致性需求:使用内存淘汰机制。例如店铺类型的查询缓存
  • 高一致性需求:主动更新,并以超时剔除作为兜底方案。例如店铺详情查询的缓存

3. 主动更新策略的三种实现业务方式

  1. 在实际开发中常使用的为方法付01

  1. 使用方法需要注意的三点问题:
  1. 点三建议使用,先操作数据库,在删除缓存

  1. 点三先删除缓存,在操作操作数据存在的线程不一致问题
  1. 正常

  1. 异常

  1. 点三 先操作数据库,在删除缓存
  1. 正常

  1. 异常(条件:缓存已经失效的场景)
  1. 此异常发生需要的条件
  1. 两个线程并行执行
  2. 线程1查询时,恰巧缓存失效,并已经查询完了数据库,准备写缓存了
  3. 在写缓存的的同时,恰巧出现了一个线程开始更新数据库

4. 总结

六 、 练习题:给查询商铺的缓存添加超时剔除和主动更新的策略

七、缓存穿透

  1. 缓存穿透:是指客户端请求的数据在缓存和数据库中都不存在。
  1. 这样缓存就不会生效,这些请求都会打到数据库中。
  1. 现象:
  1. 当用户使用多个根本不存在的ID查询数据时,因为不存在所以最后都会去查询数据库。从而导致数据库被进行大量的空访问。

1. 解决方法一:缓存空对象

  1. 特点:

  1. 流程:

2. 解决方法二:布隆过滤器

  1. 在过滤器中保证,没有的不存在的id拦截,存在的id可能到最后也是不存在
  1. 会小概率的出现缓存穿透。
  1. 特点:

  1. 流程:

3. 缓存空对象的方法实践

  1. 流程:

  1. 需要修改的地方:
  1. 如果不存在就将在redis中存储一个空对象
  2. 在判断通过key查询redis中的数据时,多加一个判断,判断查询到的数据是否为null,为null,直接结束,返回警告信息。
@Override
    public Result querById(Long id) {
        String redisId = CACHE_SHOP_KEY +id;
        // 1 先通过redis查询
        String s = stringRedisTemplate.opsForValue().get(redisId);
        if (StrUtil.isNotBlank(s)) {
            Shop shop = JSONUtil.toBean(s, Shop.class);
            return Result.ok(shop);
        }
        if (s==""){
            return Result.fail("商户不存在");
        }
        // 返回空,查询数据库
        Shop shop = getById(id);
        // 数据库返回空,返回错误信息
        if (shop==null) {
            //存储缓存空对象
            stringRedisTemplate.opsForValue().set(redisId,"",2L, TimeUnit.MINUTES); // 60分钟
            return Result.fail("商户不存在");
        }
        // 不为空返回数据并将数据存储在redis中
        stringRedisTemplate.opsForValue().set(redisId,JSONUtil.toJsonStr(shop),60L, TimeUnit.MINUTES); // 60分钟
        return Result.ok(shop);
    }

4. 总结

目录
相关文章
|
1月前
|
SQL 缓存 监控
MySQL缓存机制:查询缓存与缓冲池优化
MySQL缓存机制是提升数据库性能的关键。本文深入解析了MySQL的缓存体系,包括已弃用的查询缓存和核心的InnoDB缓冲池,帮助理解缓存优化原理。通过合理配置,可显著提升数据库性能,甚至达到10倍以上的效果。
|
5月前
|
缓存 NoSQL 关系型数据库
美团面试:MySQL有1000w数据,redis只存20w的数据,如何做 缓存 设计?
美团面试:MySQL有1000w数据,redis只存20w的数据,如何做 缓存 设计?
美团面试:MySQL有1000w数据,redis只存20w的数据,如何做 缓存 设计?
|
20天前
|
存储 NoSQL 前端开发
Redis专题-实战篇一-基于Session和Redis实现登录业务
本项目基于SpringBoot实现黑马点评系统,涵盖Session与Redis两种登录方案。通过验证码登录、用户信息存储、拦截器校验等流程,解决集群环境下Session不共享问题,采用Redis替代Session实现数据共享与自动续期,提升系统可扩展性与安全性。
126 3
Redis专题-实战篇一-基于Session和Redis实现登录业务
|
20天前
|
缓存 NoSQL 关系型数据库
Redis缓存和分布式锁
Redis 是一种高性能的键值存储系统,广泛用于缓存、消息队列和内存数据库。其典型应用包括缓解关系型数据库压力,通过缓存热点数据提高查询效率,支持高并发访问。此外,Redis 还可用于实现分布式锁,解决分布式系统中的资源竞争问题。文章还探讨了缓存的更新策略、缓存穿透与雪崩的解决方案,以及 Redlock 算法等关键技术。
|
4月前
|
缓存 监控 NoSQL
Redis 实操要点:Java 最新技术栈的实战解析
本文介绍了基于Spring Boot 3、Redis 7和Lettuce客户端的Redis高级应用实践。内容包括:1)现代Java项目集成Redis的配置方法;2)使用Redisson实现分布式可重入锁与公平锁;3)缓存模式解决方案,包括布隆过滤器防穿透和随机过期时间防雪崩;4)Redis数据结构的高级应用,如HyperLogLog统计UV和GeoHash处理地理位置。文章提供了详细的代码示例,涵盖Redis在分布式系统中的核心应用场景,特别适合需要处理高并发、分布式锁等问题的开发场景。
290 41
|
3月前
|
存储 缓存 安全
Go语言实战案例-LRU缓存机制模拟
本文介绍了使用Go语言实现LRU缓存机制的方法。LRU(最近最少使用)是一种常见缓存淘汰策略,当缓存满时,优先删除最近最少使用的数据。实现中使用哈希表和双向链表结合的方式,确保Get和Put操作均在O(1)时间内完成。适用于Web缓存、数据库查询优化等场景。
|
4月前
|
缓存 NoSQL 算法
高并发秒杀系统实战(Redis+Lua分布式锁防超卖与库存扣减优化)
秒杀系统面临瞬时高并发、资源竞争和数据一致性挑战。传统方案如数据库锁或应用层锁存在性能瓶颈或分布式问题,而基于Redis的分布式锁与Lua脚本原子操作成为高效解决方案。通过Redis的`SETNX`实现分布式锁,结合Lua脚本完成库存扣减,确保操作原子性并大幅提升性能(QPS从120提升至8,200)。此外,分段库存策略、多级限流及服务降级机制进一步优化系统稳定性。最佳实践包括分层防控、黄金扣减法则与容灾设计,强调根据业务特性灵活组合技术手段以应对高并发场景。
1064 7
|
4月前
|
机器学习/深度学习 存储 NoSQL
基于 Flink + Redis 的实时特征工程实战:电商场景动态分桶计数实现
本文介绍了基于 Flink 与 Redis 构建的电商场景下实时特征工程解决方案,重点实现动态分桶计数等复杂特征计算。通过流处理引擎 Flink 实时加工用户行为数据,结合 Redis 高性能存储,满足推荐系统毫秒级特征更新需求。技术架构涵盖状态管理、窗口计算、Redis 数据模型设计及特征服务集成,有效提升模型预测效果与系统吞吐能力。
365 2
|
5月前
|
缓存 NoSQL Java
Redis+Caffeine构建高性能二级缓存
大家好,我是摘星。今天为大家带来的是Redis+Caffeine构建高性能二级缓存,废话不多说直接开始~
737 0