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. 总结

目录
相关文章
|
4天前
|
缓存 并行计算 监控
vLLM 性能优化实战:批处理、量化与缓存配置方案
本文深入解析vLLM高性能部署实践,揭秘如何通过continuous batching、PagedAttention与前缀缓存提升吞吐;详解批处理、量化、并发参数调优,助力实现高TPS与低延迟平衡,真正发挥vLLM生产级潜力。
88 0
vLLM 性能优化实战:批处理、量化与缓存配置方案
|
1月前
|
存储 NoSQL 前端开发
Redis专题-实战篇一-基于Session和Redis实现登录业务
本项目基于SpringBoot实现黑马点评系统,涵盖Session与Redis两种登录方案。通过验证码登录、用户信息存储、拦截器校验等流程,解决集群环境下Session不共享问题,采用Redis替代Session实现数据共享与自动续期,提升系统可扩展性与安全性。
177 3
Redis专题-实战篇一-基于Session和Redis实现登录业务
|
25天前
|
缓存 负载均衡 监控
135_负载均衡:Redis缓存 - 提高缓存命中率的配置与最佳实践
在现代大型语言模型(LLM)部署架构中,缓存系统扮演着至关重要的角色。随着LLM应用规模的不断扩大和用户需求的持续增长,如何构建高效、可靠的缓存架构成为系统性能优化的核心挑战。Redis作为业界领先的内存数据库,因其高性能、丰富的数据结构和灵活的配置选项,已成为LLM部署中首选的缓存解决方案。
|
27天前
|
缓存 运维 监控
Redis 7.0 高性能缓存架构设计与优化
🌟蒋星熠Jaxonic,技术宇宙中的星际旅人。深耕Redis 7.0高性能缓存架构,探索函数化编程、多层缓存、集群优化与分片消息系统,用代码在二进制星河中谱写极客诗篇。
|
1月前
|
缓存 NoSQL 关系型数据库
Redis缓存和分布式锁
Redis 是一种高性能的键值存储系统,广泛用于缓存、消息队列和内存数据库。其典型应用包括缓解关系型数据库压力,通过缓存热点数据提高查询效率,支持高并发访问。此外,Redis 还可用于实现分布式锁,解决分布式系统中的资源竞争问题。文章还探讨了缓存的更新策略、缓存穿透与雪崩的解决方案,以及 Redlock 算法等关键技术。
|
5月前
|
缓存 NoSQL 关系型数据库
美团面试:MySQL有1000w数据,redis只存20w的数据,如何做 缓存 设计?
美团面试:MySQL有1000w数据,redis只存20w的数据,如何做 缓存 设计?
美团面试:MySQL有1000w数据,redis只存20w的数据,如何做 缓存 设计?
|
5月前
|
缓存 NoSQL Java
Redis+Caffeine构建高性能二级缓存
大家好,我是摘星。今天为大家带来的是Redis+Caffeine构建高性能二级缓存,废话不多说直接开始~
827 0
|
5月前
|
消息中间件 缓存 NoSQL
基于Spring Data Redis与RabbitMQ实现字符串缓存和计数功能(数据同步)
总的来说,借助Spring Data Redis和RabbitMQ,我们可以轻松实现字符串缓存和计数的功能。而关键的部分不过是一些"厨房的套路",一旦你掌握了这些套路,那么你就像厨师一样可以准备出一道道饕餮美食了。通过这种方式促进数据处理效率无疑将大大提高我们的生产力。
217 32
|
5月前
|
缓存 NoSQL Java
Redis:现代服务端开发的缓存基石与电商实践-优雅草卓伊凡
Redis:现代服务端开发的缓存基石与电商实践-优雅草卓伊凡
129 5
Redis:现代服务端开发的缓存基石与电商实践-优雅草卓伊凡
|
7月前
|
缓存 NoSQL Java
Redis应用—8.相关的缓存框架
本文介绍了Ehcache和Guava Cache两个缓存框架及其使用方法,以及如何自定义缓存。主要内容包括:Ehcache缓存框架、Guava Cache缓存框架、自定义缓存。总结:Ehcache适合用作本地缓存或与Redis结合使用,Guava Cache则提供了更灵活的缓存管理和更高的并发性能。自定义缓存可以根据具体需求选择不同的数据结构和引用类型来实现特定的缓存策略。
451 16
Redis应用—8.相关的缓存框架