黑马点评2——商户查询缓存(P37店铺类型查询业务添加缓存练习题答案)redis缓存、更新、穿透、雪崩、击穿、工具封装

news2024/11/15 11:49:36

文章目录

  • 什么是缓存?
  • 添加Redis缓存
    • 店铺类型查询业务添加缓存练习题
  • 缓存更新策略
    • 给查询商铺的缓存添加超时剔除和主动更新的策略
  • 缓存穿透
    • 缓存空对象
    • 布隆过滤
  • 缓存雪崩
    • 解决方案
  • 缓存击穿
    • 解决方案
    • 基于互斥锁方式解决缓存击穿问题
    • 基于逻辑过期的方式解决缓存击穿问题
  • 缓存工具封装

什么是缓存?

在这里插入图片描述

缓存也要考虑成本的问题,不是随便用的
在这里插入图片描述

添加Redis缓存

在这里插入图片描述
在这里插入图片描述


    @Override
    public Result queryById(Long id) {
        String redisKey = RedisConstants.CACHE_SHOP_KEY + id;
        // 1. 从redis查询商铺缓存
        String shopJson = stringRedisTemplate.opsForValue().get(redisKey);
        // 2. 判读是否存在
        if(StrUtil.isNotBlank(shopJson)){
            // 3. 存在,直接返回
            Shop shop = JSONUtil.toBean(shopJson, Shop.class);
            return Result.ok(shop);
        }
        // 4. 不存在,根据id查询数据库
        Shop shop = getById(id);
        // 5. 不存在,写入redis
        if(shop == null){
            return Result.fail("店铺不存在!");
        }
        // 6. 存在,写入redis
        stringRedisTemplate.opsForValue().set(redisKey,JSONUtil.toJsonStr(shop));
        // 7. 返回
        return Result.ok(shop);
    }

店铺类型查询业务添加缓存练习题

@Override
    public Result queryTypeList() {
        // 1. 从redis查询店铺类别缓存
        List<String> shopTypeRedisKey = stringRedisTemplate.opsForList().range(RedisConstants.CACHE_SHOP_TYPE_KEY,0,-1);
        // 2. 判断是否命中缓存
        if(!CollectionUtils.isEmpty(shopTypeRedisKey)){
            // 3. 存在,直接返回,即是命中缓存
            // 使用stream流将json集合转为
            List<ShopType> shopTypeList = shopTypeRedisKey.stream()
                    .map(item -> JSONUtil.toBean(item, ShopType.class))
                    .sorted(Comparator.comparingInt(ShopType::getSort))
                    .collect(Collectors.toList());
            // 返回缓存数据
            return Result.ok(shopTypeList);
        }
        // 4. 不存在,查询数据库
        List<ShopType> shopTypes = query().orderByAsc("sort").list();
        // 判断数据库中是否有数据
        if(CollectionUtils.isEmpty(shopTypes)){
            // 不存在则缓存一个空集合,解决缓存穿透
            stringRedisTemplate.opsForValue().set(RedisConstants.CACHE_SHOP_TYPE_KEY, Collections.emptyList().toString(),RedisConstants.CACHE_NULL_TTL, TimeUnit.MINUTES);
            return Result.fail("商品分类信息为空");
        }
        // 5. 数据存在,先写入redis,再返回
        // 使用stream流将bean集合转为json集合
        List<String> shopTypeCache = shopTypes.stream()
                .sorted(Comparator.comparingInt(ShopType::getSort))
                .map(item -> JSONUtil.toJsonStr(item))
                .collect(Collectors.toList());

        stringRedisTemplate.opsForList().rightPushAll(RedisConstants.CACHE_SHOP_TYPE_KEY,shopTypeCache);
        stringRedisTemplate.expire(RedisConstants.CACHE_SHOP_TYPE_KEY,RedisConstants.CACHE_SHOP_TYPE_TTL, TimeUnit.MINUTES);
        // 6. 返回(按类别升序排序)
        return Result.ok(shopTypes);
    }

缓存更新策略

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
操作缓存和数据库的顺序,不论谁先进行都可能会有线程安全的问题
在这里插入图片描述

但方案二的发生可能性更小,所以更优
总结:
在这里插入图片描述

给查询商铺的缓存添加超时剔除和主动更新的策略

在这里插入图片描述
查询店铺:

  @Override
    public Result queryById(Long id) {
        String redisKey = RedisConstants.CACHE_SHOP_KEY + id;
        // 1. 从redis查询商铺缓存
        String shopJson = stringRedisTemplate.opsForValue().get(redisKey);
        // 2. 判读是否存在
        if(StrUtil.isNotBlank(shopJson)){
            // 3. 存在,直接返回
            Shop shop = JSONUtil.toBean(shopJson, Shop.class);
            return Result.ok(shop);
        }

        // 4. 不存在,根据id查询数据库
        Shop shop = getById(id);
        // 5. 不存在,返回错误
        if(shop == null){
            return Result.fail("店铺不存在!");
        }
        // 6. 存在,写入redis
        stringRedisTemplate.opsForValue().set(redisKey,JSONUtil.toJsonStr(shop), RedisConstants.CACHE_SHOP_TTL, TimeUnit.MINUTES);
        // 7. 返回
        return Result.ok(shop);
    }

修改店铺:

@Override
    @Transactional
    public Result update(Shop shop) {
        Long id = shop.getId();
        if(id == null){
            return Result.fail("店铺id不能为空");
        }
        // 更新数据库,在删除缓存
        updateById(shop);
        // 删除缓存
        stringRedisTemplate.delete(RedisConstants.CACHE_SHOP_KEY + id);
        return Result.ok();
    }

缓存穿透

客户端请求的数据在缓存和数据库中都不存在,这样缓存永远不会生效,这些请求都会打到数据库

在这里插入图片描述

缓存空对象

在这里插入图片描述
可以设置一个TTL,解决内存消耗问题
可能存在短期不一致的问题,控制TTL的时间,可以一定程度的缓解这个问题。

布隆过滤

客户端个redis之间,在加一层过滤——布隆过滤器——哈希算法二进制位保存数据
布隆过滤器说如果不存在一定是不存在,但存在不一定是100% 的
在这里插入图片描述
先看一下之前查询商铺信息的业务流程
在这里插入图片描述
物品们采用方案一应该把空数据写入redis

在这里插入图片描述
在这里插入图片描述

缓存雪崩

在这里插入图片描述

解决方案

  • 给不同的key的TTL添加随机值——针对问题一
  • 利用redis集群提高服务的可用性——针对问题二
  • 给缓存业务添加降级限流策略
  • 给业务添加多级缓存

缓存击穿

在这里插入图片描述

解决方案

互斥锁和逻辑过期
在这里插入图片描述
在这里插入图片描述

基于互斥锁方式解决缓存击穿问题

在这里插入图片描述
获取锁:
- redis的setnx指令可以在key不存在的时候写,存在的时候不能写,就类似于互斥
释放锁:
- 删掉就行了
设置锁的时候要设置有效期,避免因为某种原因锁得不到释放

 @Override
    public Result queryById(Long id) {
        // 缓存穿透
//        Shop shop = queryWithPassThrough(id);

        // 互斥锁解决缓存击穿
        Shop shop = queryWithMutex(id);
        if(shop == null){
            return Result.fail("店铺不存在!");
        }
        // 7. 返回
        return Result.ok(shop);
    }
 /**
     * 解决缓存击穿(互斥锁)的写法
     * @param id
     * @return
     */
    public Shop queryWithMutex(Long id){
        String redisKey = RedisConstants.CACHE_SHOP_KEY + id;
        // 1. 从redis查询商铺缓存
        String shopJson = stringRedisTemplate.opsForValue().get(redisKey);
        // 2. 判读是否存在
        if(StrUtil.isNotBlank(shopJson)){
            // 3. 存在,直接返回
            return JSONUtil.toBean(shopJson, Shop.class);
        }

        // 命中的是否是空值
        if(shopJson != null){
            // 返回一个错误信息
            return null;
        }
        //4. 开始实现缓存重建
        // 4.1 获取互斥锁
        String lockKey = "lock:shop:" + id;
        Shop shop = null;
        try{
            boolean isLock = tryLock(lockKey);
            // 4.2 判断是否获取成功
            if(!isLock){
                // 4.3 如果失败,则休眠并重试
                Thread.sleep(50);
                return queryWithMutex(id);
            }

            // 4.4 如果成功,根据id查询数据库
            shop = getById(id);
            // 模拟重建的延时——测试的时候打开
//            Thread.sleep(200);
            // 5. 不存在,返回错误
            if(shop == null){
                // 将空值写入redis——解决缓存穿透
                stringRedisTemplate.opsForValue().set(redisKey,"",RedisConstants.CACHE_NULL_TTL,TimeUnit.MINUTES);
                // 返回错误信息
                return null;
            }
            // 6. 存在,写入redis
            stringRedisTemplate.opsForValue().set(redisKey,JSONUtil.toJsonStr(shop), RedisConstants.CACHE_SHOP_TTL, TimeUnit.MINUTES);
        } catch (InterruptedException e){
            throw new RuntimeException(e);
        }finally {
            // 释放互斥锁
            unLock(lockKey);
        }
        // 7. 返回
        return shop;
    }
    private boolean tryLock(String key){
        Boolean flag = stringRedisTemplate.opsForValue().setIfAbsent(key, "1", 10, TimeUnit.SECONDS);
        return BooleanUtil.isTrue(flag);  // 因为flag是封装类,而要求的返回值是基本数据类型,在返回的时候就会进行自动的拆箱,拆箱的时候会出现空指针
    }

    private void unLock(String key){
        stringRedisTemplate.delete(key);
    }

基于逻辑过期的方式解决缓存击穿问题

在这里插入图片描述

有个小问题,我们想要给存入redis的数据添加过期时间,但是我们的Shop实体类中又没有过期时间这个字段怎么办呢?
我们去给这个Shop实体添加过期时间字段可行吗?可行,但是对代码有侵入性,而且这个字段除了这里其他地方都用不到。
那怎么办?
我们可以声明一个RedisData的实体类,里面有一个过期时间的属性,让Shop继承这个实体类,Shop也就有了过期时间的属性了,但还是有一点点不好,还是需要修改源代码,需要修改Shop,有一定的侵入性,虽然也蛮好的。
还有一种方案:在RedisData中在声明一个Object的字段,把想要存储的数据放到Object中。

@Data
public class RedisData {
    private LocalDateTime expireTime;
    private Object data;
}

实际的项目肯定会有管理系统在后台点击,把热点数据提前缓存进redis,我们这里用一个单元测试完成这个功能。
先写一个缓存进redis的方法

    public void saveShop2Redis(Long id, Long expireSeconds){
        // 1. 查询店铺数据
        Shop shop = getById(id);
        // 2. 封装逻辑过期时间
        RedisData redisData = new RedisData();
        redisData.setData(shop);
        redisData.setExpireTime(LocalDateTime.now().plusSeconds(expireSeconds));
        ///3.写入redis
        stringRedisTemplate.opsForValue().set(RedisConstants.CACHE_SHOP_KEY + id, JSONUtil.toJsonStr(redisData));
    }

在编写一个单元测试

@SpringBootTest
class HmDianPingApplicationTests {

    @Autowired
    private ShopServiceImpl shopService;

    @Test
    void testSaveShop() {
        shopService.saveShop2Redis(1L, 10L);
    }

}

下面我们完成基于逻辑过期的方式解决缓存击穿的商铺查询的代码

// 使用线程池来开辟新线程
    private static final ExecutorService CACHE_REBUILD_EXECUTOR = Executors.newFixedThreadPool(10);

    /**
     * 解决缓存击穿(逻辑过期)的写法
     * @param id
     * @return
     */
    public Shop queryWithLogicalExpire(Long id){
        String redisKey = RedisConstants.CACHE_SHOP_KEY + id;
        // 1. 从redis查询商铺缓存
        String shopJson = stringRedisTemplate.opsForValue().get(redisKey);
        // 2. 判读是否存在
        if(StrUtil.isBlank(shopJson)){
            // 3. 不存在,直接返回
            return null;
        }
        // 4. 命中需要判断过期时间,需要先把json反序列化位对象
        RedisData redisData = JSONUtil.toBean(shopJson, RedisData.class);
        JSONObject jsonData = (JSONObject) redisData.getData(); // 如果不强转就是一个Object,但本质上是JSONObject,所以先转成JSONObject
        Shop shop = JSONUtil.toBean(jsonData, Shop.class);
        LocalDateTime expireTime = redisData.getExpireTime();
        // 5. 判断是否过期
        if(expireTime.isAfter(LocalDateTime.now())){
            // 5.1 未过期,直接返回店铺信息
            return shop;
        }
        // 5.2 已过期,需要缓存重建
        // 6. 缓存重建
        // 6.1 获取互斥锁
        String lockKey = RedisConstants.LOCK_SHOP_KEY + id;
        boolean isLock = tryLock(lockKey);
        // 6.2 判断是否获取锁成功
        if(isLock){
            //6.3成功 开启独立线程实现缓存重建
            CACHE_REBUILD_EXECUTOR.submit(()->{
               try {
                   // 重建缓存
                   this.saveShop2Redis(id,20L);
               }catch (Exception e){

               } finally {
                   // 释放锁
                   unLock(lockKey);
               }
            });
        }
        // 6.4 返回过期的商铺信息
        return shop;
    }

缓存工具封装

在这里插入图片描述
把封装的代码放到CacheClient这个类中,并添加@Component注解,把这个bean交给Spring管理,封装的工具类如下:


@Slf4j
@Component
public class CacheClient {


    private final StringRedisTemplate stringRedisTemplate;

    // 用构造器注入
    public CacheClient(StringRedisTemplate stringRedisTemplate){
        this.stringRedisTemplate = stringRedisTemplate;
    }

    public void set(String key, Object value, Long time, TimeUnit unit){
        stringRedisTemplate.opsForValue().set(key, JSONUtil.toJsonStr(value),time,unit);
    }

    public void setWithLogicalExpire(String key, Object value, Long time, TimeUnit unit){
        // 设置逻辑过期
        RedisData redisData = new RedisData();
        redisData.setData(value);
        redisData.setExpireTime(LocalDateTime.now().plusSeconds(unit.toSeconds(time)));
        // 写入Redis
        stringRedisTemplate.opsForValue().set(key, JSONUtil.toJsonStr(redisData));
    }


    public <R, ID> R queryWithPassThrough(String keyPrefix, ID id, Class<R> type, Function<ID, R> dbFallback, Long time, TimeUnit unit){
        String redisKey = keyPrefix + id;
        // 1. 从redis查询商铺缓存
        String json = stringRedisTemplate.opsForValue().get(redisKey);
        // 2. 判读是否存在
        if(StrUtil.isNotBlank(json)){
            // 3. 存在,直接返回
            return JSONUtil.toBean(json, type);
        }

        // 命中的是否是空值
        if(json != null){
            // 返回一个错误信息
            return null;
        }

        // 4. 不存在,根据id查询数据库——我们哪知道去查哪个数据库,只能调用者告诉我们,——函数式编程
        R r = dbFallback.apply(id);
        // 5. 不存在,返回错误
        if(r == null){
            // 将空值写入redis——解决缓存穿透
            stringRedisTemplate.opsForValue().set(redisKey,"",RedisConstants.CACHE_NULL_TTL,TimeUnit.MINUTES);
            // 返回错误信息
            return null;
        }
        // 6. 存在,写入redis
        this.set(redisKey, r, time, unit);
        // 7. 返回
        return r;
    }

    private static final ExecutorService CACHE_REBUILD_EXECUTOR = Executors.newFixedThreadPool(10);

    public <R,ID> R queryWithLogicalExpire(String keyPrefix, ID id, Class<R> type, Function<ID, R> dbFallback, Long time, TimeUnit unit){
        String redisKey = keyPrefix + id;
        // 1. 从redis查询商铺缓存
        String json = stringRedisTemplate.opsForValue().get(redisKey);
        // 2. 判读是否存在
        if(StrUtil.isBlank(json)){
            // 3. 不存在,直接返回
            return null;
        }
        // 4. 命中需要判断过期时间,需要先把json反序列化位对象
        RedisData redisData = JSONUtil.toBean(json, RedisData.class);
        JSONObject jsonData = (JSONObject) redisData.getData(); // 如果不强转就是一个Object,但本质上是JSONObject,所以先转成JSONObject
        R r = JSONUtil.toBean(jsonData, type);
        LocalDateTime expireTime = redisData.getExpireTime();
        // 5. 判断是否过期
        if(expireTime.isAfter(LocalDateTime.now())){
            // 5.1 未过期,直接返回店铺信息
            return r;
        }
        // 5.2 已过期,需要缓存重建
        // 6. 缓存重建
        // 6.1 获取互斥锁
        String lockKey = RedisConstants.LOCK_SHOP_KEY + id;
        boolean isLock = tryLock(lockKey);
        // 6.2 判断是否获取锁成功
        if(isLock){
            //6.3成功 开启独立线程实现缓存重建
            CACHE_REBUILD_EXECUTOR.submit(()->{
                try {
                    // 重建缓存
                    // 先查数据库
                    R r1 = dbFallback.apply(id);
                    // 写入redis
                    this.setWithLogicalExpire(redisKey, r1, time, unit);
                }catch (Exception e){

                } finally {
                    // 释放锁
                    unLock(lockKey);
                }
            });
        }
        // 6.4 返回过期的商铺信息
        return r;
    }


    private boolean tryLock(String key){
        Boolean flag = stringRedisTemplate.opsForValue().setIfAbsent(key, "1", 10, TimeUnit.SECONDS);
        return BooleanUtil.isTrue(flag);  // 因为flag是封装类,而要求的返回值是基本数据类型,在返回的时候就会进行自动的拆箱,拆箱的时候会出现空指针
    }

    private void unLock(String key){
        stringRedisTemplate.delete(key);
    }


}

封装这个工具类,有很多的技巧要总结:

  1. 传递的参数和返回的数据类型要泛型
  2. 函数式编程:在封装queryWithPassThrough的时候,里面在redis查询不存在的时候,我们要去查询数据库,那查询数据库的代码,我们泛型传递的参数,调用哪个查询数据库的函数去查询数据库呢?这时要用函数式编程,把要用到的函数通过参数传递过来,有参数有返回值就用Function<ID, R> dbFallback,使用的时候直接R r = dbFallback.apply(id);即可,调用这个工具方法的时候把具体的查询函数作为参数传进去。

那这些工具类在调用的时候又该怎么调用呢?

  @Override
    public Result queryById(Long id) {
        // 缓存穿透
//        Shop shop = cacheClient.queryWithPassThrough(RedisConstants.CACHE_SHOP_KEY, id, Shop.class, this::getById, RedisConstants.CACHE_SHOP_TTL, TimeUnit.MINUTES);

        // 逻辑过期解决缓存击穿问题
        Shop shop = cacheClient.queryWithLogicalExpire(RedisConstants.CACHE_SHOP_KEY, id, Shop.class, this::getById, 20L, TimeUnit.SECONDS);

        if(shop == null){
            return Result.fail("店铺不存在!");
        }
        // 7. 返回
        return Result.ok(shop);
    }

那我们的缓存击穿想测试的话,还是得先用单元测试的方法,先往redis中写入点热点数据,现在就可以改进我们的单元测试代码


@SpringBootTest
class HmDianPingApplicationTests {
    @Autowired
    private CacheClient cacheClient;

    @Test
    void testSaveShop() {
        Shop shop = shopService.getById(1L);
        cacheClient.setWithLogicalExpire(RedisConstants.CACHE_SHOP_KEY + 1L,shop,10L, TimeUnit.SECONDS);
    }
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2104943.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

DrissionPage设置启动浏览器为edge

1.查看浏览器启动路径 在浏览器地址栏输入下面地址&#xff0c;拿到可执行文件的路径 。 edge://version/ 2.替换路径 打开DrissionPage._configs. chromium_options.py文件&#xff0c;找到def browser_path(self)这个函数&#xff0c;将返回内容替换为edge的启动路径&#x…

xacro->urdf->pdf

在ROS 2系统中&#xff0c;要将xacro文件转换为PDF文件&#xff0c;可以按照以下步骤操作&#xff1a; 步骤1&#xff1a;将Xacro文件转换为URDF文件 首先&#xff0c;需要将xacro文件转换为urdf文件。可以使用ROS 2提供的xacro工具来完成这个转换。 ros2 run xacro xacro p…

6.1.数据结构-c/c++模拟实现堆上篇(向下,上调整算法,建堆,增删数据)

目录 一.堆(Heap)的基本介绍 二.堆的常用操作&#xff08;以小根堆为例&#xff09; 三.实现代码 3.1 堆结构定义 3.2 向下调整算法* 3.3 初始化堆* 3.4 销毁堆 3.4 向上调整算法* 3.5 插入数据 3.6 删除数据 3.7 返回堆顶数据 四.下篇内容 1.堆排序 2.TopK问题 一…

案例-KVM+GFS分布式存储系统构建KVM高可用(虚拟化实战)

NFS GlusterFS 基于共享存储 采用GFS做共享存储&#xff1b; 实验环境&#xff1a;101 102 103 104 做gfs集群&#xff1b;201 202做虚拟机&#xff1b; 同步一下会话&#xff1b; 为了方便使用主机名进行通信&#xff0c;修改hosts文件&#xff1b; 为了使用GlusterFS的仓库…

国内独家首发 | OpenCSG开源中文版fineweb edu数据集

01 背景 近年来&#xff0c;人工智能&#xff08;AI&#xff09;技术&#xff0c;特别是自然语言处理&#xff08;NLP&#xff09;的飞速发展深刻影响着各个行业。从智能客服到内容生成&#xff0c;从语音识别到翻译工具&#xff0c;NLP的应用已经无处不在。在这一领域中&…

[线程]JUC中常见的类 及 集合类在多线程下的线程安全问题

文章目录 一. JUC中常见的类1. Callable接口2. ReentrantLock3. Semaphore 信号量4. CountDownLatch 二. 集合类在多线程下的线程安全问题多线程下使用ArrayList多线程下使用哈希表(重要) 下面介绍的内容是面试中常考, 但是实际开发中用不到的知识 一. JUC中常见的类 JUC : ja…

『功能项目』主角身旁召唤/隐藏坐骑【20】

本章项目成果展示 我们打开上一篇19坐骑UI搭建及脚本控制显/隐的项目&#xff0c; 本章要做的事情是在坐骑UI界面点击召唤及隐藏坐骑的功能 首先在外包中拖拽一个坐骑熊的预制体 完全解压缩 重命名为MountBear 在资源文件夹Resources下的/预制体文件夹Prefabs下新建坐骑文件夹…

海外合规|新加坡网络安全认证计划简介(三)-Cyber Trust

一、 认证简介&#xff1a; Cyber Trust标志是针对数字化业务运营更为广泛的组织的网络安全认证。该标志针对的是规模较大或数字化程度较高的组织&#xff0c;因为这些组织可能具有更高的风险水平&#xff0c;需要他们投资专业知识和资源来管理和保护其 IT 基础设施和系统。Cy…

新质生产力人工智能+系列5-智能业务识别研究(含任务、数据、算力资源)

在新质生产力高质量发展的要求下&#xff0c;中国移动在“人工智能”和 “数据要素X”方面不断发力&#xff0c;持续发布高质量电信数据集。围绕网元智能、运维智能、服务智能三大方向建设&#xff0c;涵盖无线信道、基站、云网、核心网、哑资源等多领域&#xff0c;支持感知、…

护工系统|护工陪护系统|护工小程序

在医疗服务行业日新月异的今天&#xff0c;陪护机构正乘着数字化转型的东风&#xff0c;扬帆远航&#xff0c;其中&#xff0c;护工系统的引入无疑为其插上了一对强劲的翅膀&#xff0c;引领着行业向更加高效、精细化的方向迈进。这一系统不仅是对传统陪护模式的深刻重塑&#…

oracle物理存储结构文件详解

文章目录 oracle物理文件结构图① 控制文件&#xff1a;② 数据文件&#xff1a;③ 联机Redo日志文件&#xff1a;④ 参数文件&#xff1a;⑤ 归档文件&#xff1a;⑥ 密码文件&#xff1a; oracle物理文件结构图 Oracle数据库的物理结构由控制文件&#xff08;Control f…

硬件工程师笔试面试知识器件篇——电容

目录 电容 2.1、基础 电容原理图 电容实物图 2.1.1、定义 2.1.2、原理 2.1.3、电容的类型 分类1: 分类2: 2.1.4、电容的应用 2.2、相关问题 2.2.1、电容器的电容值如何测量 2.2.2、不同类型的电容器在实际应用中有那些具体差异 2.2.3、如何选择合适的电容器来满…

OrangePi AIpro 香橙派 昇腾 Ascend C 算子开发 与 调用 - Tiling实现 2

OrangePi AIpro 香橙派 昇腾 Ascend C 算子开发 与 调用 - Tiling实现 2 flyfish 前置知识 1 前置知识 2 Host侧CPU和Device侧NPU的主要区别 不同的硬件资源 CPU是为了执行通用计算任务而设计的&#xff0c;但在处理大量的并行计算&#xff08;如矩阵乘、批数据处理&#…

智能优化算法-北方苍鹰优化算法(NGO)(附源码)

目录 1.内容介绍 2.部分代码 3.实验结果 4.内容获取 1.内容介绍 北方苍鹰优化算法 (Northern Goshawk Optimizer, NGO) 是一种基于群体智能的元启发式优化算法&#xff0c;它模拟了北方苍鹰&#xff08;Northern Goshawk&#xff09;的捕食行为、领地行为以及社交互动&#x…

input系统之InputDispatcher

往期文章&#xff1a; Input系统之IMS的启动流程 input系统之InputReader 1.概述 InputReader和InputDispatcher是Input系统的重要组成部分&#xff0c;InputReader主要负责从设备节点获取原始输入事件&#xff0c;并将封装好的事件交给InputDispatcher&#xff1b;InputDis…

数据库 | 子查询 | sql执行顺序 | mysql是否运行

1.系统&#xff08;客户端&#xff09;访问 MySQL 服务器前&#xff0c;做 的第一件事就是建立 TCP 连接。 Caches & Buffers&#xff1a; 查询缓存组件SQL Interface: SQL接口 接收用户的SQL命 令&#xff0c;并且返回用户需要查询的结果。比如 SELECT … FROM就是调用SQ…

防止goroutine崩溃导致主进程崩溃

在Go语言中&#xff0c;当一个goroutine发生异常时&#xff0c;它会直接退出&#xff0c;并不会影响其他goroutine或者主进程。Go语言的设计哲学是“不要通过共享内存来通信&#xff0c;而应该通过通信来共享内存”。这就意味着&#xff0c;goroutine之间的协作通常是通过chann…

Azure AI Search 中的二进制量化:优化存储和加快搜索速度

随着组织继续利用生成式 AI 的强大功能来构建检索增强生成 (RAG) 应用程序和代理&#xff0c;对高效、高性能和可扩展解决方案的需求从未如此强烈。 今天&#xff0c;我们很高兴推出二进制量化&#xff0c;这项新功能可将向量大小减少高达 96%&#xff0c;同时将搜索延迟减少高…

2024.9.4 作业

自己实现栈和队列的全类型 代码&#xff1a; /*******************************************/ 文件名&#xff1a;sq.h /*******************************************/ #ifndef SQ_H #define SQ_H #include <iostream> #include <stdexcept> using namespace…

文档一键生成组织架构图,这款在线AI工具让你事半功倍!

在呈现公司组织架构或部门层级时&#xff0c;经常会用到组织架构图&#xff0c;组织架构图是一种直观的可视化工具&#xff0c;用于展示一个组织内部的层级结构和各部门之间的关系。 在制作组织架构图时&#xff0c;如果拿到的是清晰的人员关系&#xff0c;绘制起来还比较简单…