项目收获总结--本地缓存方案选型及使用缓存的坑

本地缓存方案选型及使用缓存的坑

        • 一、摘要
        • 二、本地缓存
        • 三、本地缓存实现方案
          • 3.1 自己编程实现一个缓存
          • 3.2 基于 Guava Cache 实现本地缓存
          • 3.3 基于 Caffeine 实现本地缓存
          • 3.4 基于 Encache 实现本地缓存
          • 3.5 小结
        • 四、使用缓存的坑
          • 4.1 缓存穿透
          • 4.2 缓存击穿
          • 4.3 缓存雪崩
          • 4.4 数据不一致
          • 4.5 大key问题
          • 4.6 热key问题
          • 4.7 命中率问题

一、摘要

在互联网公司面试时,说到缓存,面试官基本上会绕不开的几个话题:项目中哪些地方用到了缓存?为什么要使用缓存?怎么使用它的?引入缓存后会带来哪些问题?
在这里插入图片描述

引入缓存,其实主要有两个用途:高性能、高并发

性能体现在引入缓存之前,以商城网站为例,频繁的从数据库里面获取商品数据,也就需要频繁执行SQL等待结果,若数据量很大同时请求频次逐渐增高,响应就逐渐缓慢;引入缓存之后,将数据库里面查询出来的商品数据信息存入缓存,需要时直接从缓存服务获取结果,效率极大提升。

并发体现在引入缓存之前,以 MySQL数据库为例,单台机器一秒内的请求次数到达 2000 之后就会开始报警;引入缓存之后,比如以 Redis 缓存服务器为例,单台机器一秒内的请求次数支持 110000 次,两者支持的并发量完全不是一个数量级的。

缓存和数据库效率差距大的根本原因:缓存数据存储在内存,数据库数据存储在磁盘,
而计算机中内存的数据读写性能远超磁盘的读写性能。但电脑重启后内存数据易丢失,而磁盘数据不易丢失。

所以数据存储方案不同,造就不同的实践用途。接下来就浅谈缓存,主要是本地缓存的使用。

二、本地缓存

从缓存面向的对象不同,缓存分为:本地缓存分布式缓存和多级缓存
(1)本地缓存:在单个计算机服务实例中,直接把数据缓存到内存中进行使用。
(2)分布式缓存:将一个计算机服务,同时在多台计算机里部署,所需数据无法共享(比如session会话)而引入一个独立部署的缓存服务来连接多台服务器的技术实践方案。
(3)多级缓存:在实际的业务中,本地缓存和分布式缓存会同时结合进行使用,当收到访问某个数据的操作时,会优先从本地缓存服务(一级缓存)查询,如果没有,再从分布式缓存服务(二级缓存)里面获取,如果也没有,最后再从数据库里面获取;从数据库查询完成之后,在依次更新分布式缓存服务、本地缓存服务的技术实践方案。

三、本地缓存实现方案

缓存关注点:第一是内存持久化;第二是支持缓存的数据自动过期清除。

3.1 自己编程实现一个缓存

对于简单的数据缓存,完全可以自行编写一套缓存服务。实现思路很简单:采用ConcurrentHashMap作为缓存数据存储服务,然后开启一个定时调度,每隔500毫秒检查一下过期的缓存数据,然后清除。
首先创建一个缓存实体类:

public class CacheEntity {

    /**
     * 缓存键
     */
    private String key;

    /**
     * 缓存值
     */
    private Object value;

    /**
     * 过期时间
     */
    private Long expireTime;

    //...set、get
}

接着,创建一个缓存操作工具类CacheUtils:

public class CacheUtils {

    /**
     * 缓存数据
     */
    private final static Map<String, CacheEntity> CACHE_MAP = new ConcurrentHashMap<>();

    /**
     * 定时器线程池,用于清除过期缓存
     */
    private static ScheduledExecutorService executor = Executors.newSingleThreadScheduledExecutor();

    static {
        // 注册一个定时线程任务,服务启动1秒之后,每隔500毫秒执行一次
        executor.scheduleAtFixedRate(new Runnable() {
            @Override
            public void run() {
                // 清理过期缓存
                clearCache();
            }
        },1000,500,TimeUnit.MILLISECONDS);
    }

    /**
     * 添加缓存
     * @param key    缓存键
     * @param value  缓存值
     */
    public static void put(String key, Object value){
        put(key, value, 0);
    }

    /**
     * 添加缓存
     * @param key    缓存键
     * @param value  缓存值
     * @param expire 缓存时间,单位秒
     */
    public static void put(String key, Object value, long expire){
        CacheEntity cacheEntity = new CacheEntity()
                .setKey(key)
                .setValue(value);
        if(expire > 0){
            Long expireTime = System.currentTimeMillis() + Duration.ofSeconds(expire).toMillis();
            cacheEntity.setExpireTime(expireTime);
        }
        CACHE_MAP.put(key, cacheEntity);
    }


    /**
     * 获取缓存
     * @param key
     * @return
     */
    public static Object get(String key){
        if(CACHE_MAP.containsKey(key)){
            return CACHE_MAP.get(key).getValue();
        }
        return null;
    }

    /**
     * 移除缓存
     * @param key
     */
    public static void remove(String key){
        if(CACHE_MAP.containsKey(key)){
            CACHE_MAP.remove(key);
        }
    }

    /**
     * 清理过期的缓存数据
     */
    private static void clearCache(){
        if(CACHE_MAP.size() > 0){
            return;
        }
        Iterator<Map.Entry<String, CacheEntity>> iterator = CACHE_MAP.entrySet().iterator();
        while (iterator.hasNext()){
            Map.Entry<String, CacheEntity> entry = iterator.next();
            if(entry.getValue().getExpireTime() != null && entry.getValue().getExpireTime().longValue() > System.currentTimeMillis()){
                iterator.remove();
            }
        }
    }
}

最后,创建测试main方法:

/ 写入缓存数据,过期时间为3CacheUtils.put("userName", "张三", 3);

// 读取缓存数据
Object value1 = CacheUtils.get("userName");
System.out.println("第一次查询结果:" + value1);

// 停顿4秒
Thread.sleep(4000);

// 读取缓存数据
Object value2 = CacheUtils.get("userName");
System.out.println("第二次查询结果:" + value2);

结果:

第一次查询结果:张三
第二次查询结果:null
3.2 基于 Guava Cache 实现本地缓存

Guava 是 Google 团队开源的一款 Java 核心增强库,包含集合、并发原语、缓存、IO、反射等工具箱,性能和稳定性上都有保障,应用十分广泛。而Guava Cache 很强大,支持很多特性如下:

支持最大容量限制
支持两种过期删除策略(插入时间和读取时间)
支持简单的统计功能
基于 LRU 算法实现

首先pom.xml引入guava依赖:

<!--guava-->
<dependency>
    <groupId>com.google.guava</groupId>
    <artifactId>guava</artifactId>
    <version>31.1-jre</version>
</dependency>

使用:

// 创建一个缓存实例
Cache<String, String> cache = CacheBuilder.newBuilder()
        // 初始容量
        .initialCapacity(5)
        // 最大缓存数,超出淘汰
        .maximumSize(10)
        // 过期时间
        .expireAfterWrite(3, TimeUnit.SECONDS)
        .build();

// 写入缓存数据
cache.put("userName", "张三");

// 读取缓存数据
String value1 = cache.get("userName", () -> {
    // 如果key不存在,会执行回调方法
    return "key已过期";
});
System.out.println("第一次查询结果:" + value1);

// 停顿4秒
Thread.sleep(4000);

// 读取缓存数据
String value2 = cache.get("userName", () -> {
    // 如果key不存在,会执行回调方法
    return "key已过期";
});
System.out.println("第二次查询结果:" + value2);

输出结果:

第一次查询结果:张三
第二次查询结果:key已过期
3.3 基于 Caffeine 实现本地缓存

Caffeine 是基于 java8 实现的新一代缓存工具,缓存性能接近理论最优,可以看作是 Guava Cache 的增强版,功能上两者类似,不同的是 Caffeine 采用了一种结合 LRU、LFU 优点的算法:W-TinyLFU,在性能上有明显的优越性。
首先pom.xml引入caffeine依赖:

<!--caffeine-->
<dependency>
    <groupId>com.github.ben-manes.caffeine</groupId>
    <artifactId>caffeine</artifactId>
    <version>2.9.3</version>
</dependency>

使用:

// 创建一个缓存实例
Cache<String, String> cache = Caffeine.newBuilder()
        // 初始容量
        .initialCapacity(5)
        // 最大缓存数,超出淘汰
        .maximumSize(10)
        // 设置缓存写入间隔多久过期
        .expireAfterWrite(3, TimeUnit.SECONDS)
        // 设置缓存最后访问后间隔多久淘汰,实际很少用到
        //.expireAfterAccess(3, TimeUnit.SECONDS)
        .build();

// 写入缓存数据
cache.put("userName", "张三");

// 读取缓存数据
String value1 = cache.get("userName", (key) -> {
    // 如果key不存在,会执行回调方法
    return "key已过期";
});
System.out.println("第一次查询结果:" + value1);

// 停顿4秒
Thread.sleep(4000);

// 读取缓存数据
String value2 = cache.get("userName", (key) -> {
    // 如果key不存在,会执行回调方法
    return "key已过期";
});
System.out.println("第二次查询结果:" + value2);

输出结果:

第一次查询结果:张三
第二次查询结果:key已过期
3.4 基于 Encache 实现本地缓存

Encache 是一个纯 Java 的进程内缓存框架,具有快速、精干等特点,是 Hibernate 中默认的 CacheProvider。

同 Caffeine 和 Guava Cache 相比,Encache 的功能更加丰富,扩展性更强,特性如下:

支持多种缓存淘汰算法,包括 LRULFUFIFO
缓存支持堆内存储、堆外存储、磁盘存储(支持持久化)三种
支持多种集群方案,解决数据共享问题

首先pom.xml引入ehcache依赖:

<!--ehcache-->
<dependency>
    <groupId>org.ehcache</groupId>
    <artifactId>ehcache</artifactId>
    <version>3.9.7</version>
</dependency>

使用:

/**
 * 自定义过期策略实现
 */
public  class CustomExpiryPolicy<K, V> implements ExpiryPolicy<K, V> {

    private final Map<K, Duration> keyExpireMap = new ConcurrentHashMap();


    public Duration setExpire(K key, Duration duration) {
        return keyExpireMap.put(key, duration);
    }

    public Duration getExpireByKey(K key) {
        return Optional.ofNullable(keyExpireMap.get(key))
                .orElse(null);
    }

    public Duration removeExpire(K key) {
        return keyExpireMap.remove(key);
    }

    @Override
    public Duration getExpiryForCreation(K key, V value) {
        return Optional.ofNullable(getExpireByKey(key))
                .orElse(Duration.ofNanos(Long.MAX_VALUE));
    }

    @Override
    public Duration getExpiryForAccess(K key, Supplier<? extends V> value) {
        return getExpireByKey(key);
    }

    @Override
    public Duration getExpiryForUpdate(K key, Supplier<? extends V> oldValue, V newValue) {
        return getExpireByKey(key);
    }
}
public static void main(String[] args) throws InterruptedException {
    String userCache = "userCache";

    // 自定义过期策略
    CustomExpiryPolicy<Object, Object> customExpiryPolicy = new CustomExpiryPolicy<>();

    // 声明一个容量为20的堆内缓存配置
    CacheConfigurationBuilder configurationBuilder = CacheConfigurationBuilder
            .newCacheConfigurationBuilder(String.class, String.class, ResourcePoolsBuilder.heap(20))
            .withExpiry(customExpiryPolicy);

    // 初始化一个缓存管理器
    CacheManager cacheManager = CacheManagerBuilder.newCacheManagerBuilder()
            // 创建cache实例
            .withCache(userCache, configurationBuilder)
            .build(true);

    // 获取cache实例
    Cache<String, String> cache = cacheManager.getCache(userCache, String.class, String.class);
    // 获取过期策略
    CustomExpiryPolicy expiryPolicy = (CustomExpiryPolicy)cache.getRuntimeConfiguration().getExpiryPolicy();

    // 写入缓存数据
    cache.put("userName", "张三");
    // 设置3秒过期
    expiryPolicy.setExpire("userName", Duration.ofSeconds(3));

    // 读取缓存数据
    String value1 = cache.get("userName");
    System.out.println("第一次查询结果:" + value1);

    // 停顿4秒
    Thread.sleep(4000);

    // 读取缓存数据
    String value2 = cache.get("userName");
    System.out.println("第二次查询结果:" + value2);
}

输出结果:

第一次查询结果:张三
第二次查询结果:null
3.5 小结

在这里插入图片描述
对于本地缓存的技术选型,推荐采用 Caffeine,性能上遥遥领先。功能与Guava 类似,而Encache虽支持持久化和集群,但不如分布式缓存中间件Redis。

四、使用缓存的坑

在项目中经常会使用缓存,但用不好的话坑也挺多的:
在这里插入图片描述

4.1 缓存穿透

用户请求的id在缓存中不存在恶意用户伪造不存在的id发起请求,每次从缓存中都查不到数据,而需要查询数据库,同时数据库中也没有查到该数据,也没法放入缓存。也就是每次这个用户请求过来的时候,都要查询一次数据库。
很显然,缓存根本没起作用,好像被穿透一样,每次都会去访问数据库,而直接请求数据库数量非常多,数据库可能因为扛不住压力而崩溃。
解决方案: 缓存空值
当某个用户id在缓存中查不到,在数据库中也查不到时,也要将该用户id缓存起来,只不过值是空的。这样后面的请求,再拿相同的用户id发起请求时,就能从缓存中获取空数据,直接返回而无需再去查数据库。
比如redis:

redisTemplate.opsForValue().set(key, "", CACHE_NULL_TTL, TimeUnit.MINUTES);
4.2 缓存击穿

在访问热点数据时,该热点在缓存中过期失效,导致这些大量请求短时间都直接怼到数据库,可能会造成瞬间数据库压力过大,而直接挂掉。
解决方案:
(1)加锁。在访问数据库时加锁,防止多个相同keyId的请求同时访问数据库。

try {
  String result = jedis.set(keyId, requestId, "NX", "PX", expireTime);
  if ("OK".equals(result)) {
    return queryInfoById(keyId);
  }
} finally{
    unlock(keyId,requestId);
}  
return null;

(2)自动续期
在key快要过期之前,用job给指定key自动续期。比如redis使用lua脚本。
(3)永久有效
对于很多热门key,其实是可以不用设置过期时间,让其永久有效的。

4.3 缓存雪崩

而缓存雪崩是缓存击穿的升级版,缓存击穿说的是某一个热门key失效了,而缓存雪崩说的是有多个热门key同时失效。
缓存雪崩目前有两种:

1)有大量的热门缓存,同时失效。会导致大量的请求,访问数据库。而数据库很有可能因为扛不住压力,而直接挂掉。
(2)缓存服务器down机,可能是机器硬件问题,或者机房网络问题。总之,造成了整个缓存的不可用。

解决方案:
(1) 过期时间加随机数,不要设置相同的过期时间,可以在设置的过期时间基础上,再加个1~60秒的随机数。

实际过期时间 = 过期时间 + 1~60秒的随机数

(2)保证高可用
比如:如果使用了redis,可以使用哨兵模式,或者集群模式,避免出现单节点故障导致整个redis服务不可用的情况。

(3)服务降级
需要配置一些默认的兜底数据。程序中有个全局开关,比如有10个请求在最近一分钟内,从redis中获取数据失败,则全局开关打开。后面的新请求,就直接从配置中心中获取默认的数据。

4.4 数据不一致

数据库和缓存(比如:redis)双写数据一致性问题,是一个跟开发语言无关的公共问题。尤其高并发场景这个问题尤为严重。
解决方案
先写数据库,再删缓存!
先写数据库,再删缓存!
先写数据库,再删缓存!
除非同时满足:

缓存刚好自动失效。
读请求从数据库查出旧值,更新缓存的耗时,比写请求写数据库,并且删除缓存的还长。

才会出现数据不一致,但系统同时满足上述两个条件的概率非常小。

4.5 大key问题

在使用缓存的时候,特别是Redis,经常会遇到大key问题(缓存中单个key的value值过大)。
项目经历:

在一个风控项目中曾开发过一个分类树查询接口,系统刚上线时,数据量少,在Redis中定义的key比较小,
我在做系统设计时,也没考虑到这个问题。系统运行很长一段时间也没有问题。但随着时间的推移,用户的数据越来越多,
用户的购买行为分类树也越来越大,慢慢形成大key问题。后来某一天之后发现,线上查询客户画像接口耗时越来越长,
追查原因,发现单个用户分类数据涨到上万个,导致该接口出现性能问题,追查发现分类树json串已经接近16MB,而引发大key问题导致的。

解决方案:
(1)缩减字段名
优化在Redis中存储数据的大小,首先需要对数据进行瘦身。只保存需要用到的字段:

@AllArgsConstructor
@Data
public class Category {

    private Long id;
    private String name;
    private Long parentId;
    private Date inDate;
    private Long inUserId;
    private String inUserName;
    private List<Category> children;
}

这个分类对象中inDate、inUserId和inUserName字段是可以不用保存的。
然后,修改自动名称:

@AllArgsConstructor
@Data
public class Category {
    /**
     * 分类编号
     */
    @JsonProperty("i")
    private Long id;

    /**
     * 分类层级
     */
    @JsonProperty("l")
    private Integer level;

    /**
     * 分类名称
     */
    @JsonProperty("n")
    private String name;

    /**
     * 父分类编号
     */
    @JsonProperty("p")
    private Long parentId;

    /**
     * 子分类列表
     */
    @JsonProperty("c")
    private List<Category> children;
}

由于在一万多条数据中,每条数据的字段名称是固定的,他们的重复率太高,由此,可以在json序列化时,改成一个简短的名称,以便于返回更少的数据大小。

(2)压缩数据
由于在Redis中保存的key/value,其中的value我是存储json格式的字符串,但是占用内存很大,所以需要对存储的数据做压缩。

由于RedisTemplate支持,value保存byte数组,因此先将json字符串数据用GZip工具类压缩成byte数组,然后保存到Redis中。

在获取数据时,将byte数组转换成json字符串,然后再转换成分类树。

这样优化之后,保存到Redis中的分类树的数据大小减少10倍,从而解决大key问题。

4.6 热key问题

二八原理描述:80%的用户经常访问20%的热点数据。引发数据倾斜,不能均匀分布,尤其是高并发系统中问题比较大。

比如有个促销系统,有几款商品性价比非常高,这些商品数据在Redis中按分片保存的,不同的数据保存在不同的服务器节点上。
如果用户疯狂抢购其中3款商品,而这3款商品正好保存在同一台Redis服务端节点。
这样会出现大量的用户请求集中访问同一天Redis服务器节点,该节点很有可能会因为扛不住这么大的压力,而直接down机。

解决方案:
(1)拆分key:提前做好评估,将热点数据分开存储在不同redis服务器来分摊压力。
(2)增加本地缓存:对于热key数据,可以增加一层本地缓存(见前文),能够提升性能的同时也能避免Redis访问量过大的问题。但可能会出现数据不一致问题。

4.7 命中率问题

前面的情况都影响缓存的命中率问题,因为可能会出现缓存不存在,或者缓存过期等问题,导致缓存不能命中。
解决方案:
(1)缓存预热
在API服务启动之前,可以先用job,将相关数据先保存到缓存中,做预热。
这样后面的请求,就能直接从缓存中获取数据,而无需访问数据库。
(2)合理调整过期时间
(3)增加缓存内存

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

游戏的无边框模式是什么?有啥用?

现在很多游戏的显示设置中&#xff0c;都有个比较特殊的选项“无边框”。小伙伴们如果尝试过&#xff0c;就会发现这个效果和全屏几乎一毛一样&#xff0c;于是就很欢快地用了起来&#xff0c;不过大家也许会发现&#xff0c;怎么和全屏比起来&#xff0c;似乎有点不够爽快&…

【2024_CUMCM】时间序列1

目录 概念 时间序列数据 时期和时点时间序列 数值变换规律 长期趋势T 季节趋势S 循环变动C 不规则变动I 叠加和乘积模型 叠加模型 相互独立 乘积模型 相互影响 注 spss缺失值填补 简单填补 五种填补方法 填补原则 1.随机缺失 2.完全随机缺失 3.非随机缺失…

HarmonyOS NEXT:一次开发,多端部署

寄语 这几年特别火的uni-app实现了“一次开发&#xff0c;多端使用”&#xff0c;它这个端指的是ios、安卓、各种小程序这些&#xff0c;而HarmonyOS NEXT也提出了“一次开发&#xff0c;多端部署”&#xff0c;而它这个端指的是终端设备&#xff0c;也就是我们的手机、平板、电…

Java面试题:MVCC

MVCC 保证事务的隔离性 排它锁: 一个事务获取了数据行的排他锁,其他事务就不能再获取该行的其他锁 MVCC: 多版本并发控制 维护一个数据的多个版本,使读写不存在冲突 具体实现依靠 隐藏字段 mysql中隐藏了三个隐藏字段 db_trx_id:最近修改事务 db_roll_ptr:指向上一个…

【Leetcode】最小数字游戏

你有一个下标从 0 开始、长度为 偶数 的整数数组 nums &#xff0c;同时还有一个空数组 arr 。Alice 和 Bob 决定玩一个游戏&#xff0c;游戏中每一轮 Alice 和 Bob 都会各自执行一次操作。游戏规则如下&#xff1a; 每一轮&#xff0c;Alice 先从 nums 中移除一个 最小 元素&…

[linux]IO多路复用机制:select、poll、epoll

为什么需要IO多路复用 首先我要向大家输出一个IO的概念&#xff1a;IO在我看来就是 等 拷贝&#xff08;简化IO模型&#xff09;&#xff0c;等就是等待系统资源&#xff08;设备。数据等&#xff09;就绪&#xff08;比如等待文件描述符就绪&#xff0c;等待数据就绪&#x…

Linux开发:Fuse介绍

Fuse(filesystem in userspace),是一个用户空间的文件系统。通过fuse内核模块的支持&#xff0c;开发者只需要根据fuse提供的接口实现具体的文件操作时所对应的回调函数&#xff0c;就可以实现一个文件系统。由于其主要实现代码位于用户空间中&#xff0c;因此不需要重新编译内…

springboot+vue 开发记录(九)后端打包部署运行

本篇文章主要内容是后端项目写好了&#xff0c;怎么打包部署到服务器上运行。 文章目录 1. 在服务器上安装Docker2. 在Docker中装MySQL3. 在Docker中设置网桥&#xff0c;实现容器间的网络通信4. 修改后端配置文件5. 修改pom.xml文件6. 打包7. 编写DockerFile文件8. 上传文件到…

【调试笔记-20240713-Windows-Tauri 多个HTML页面支持】

调试笔记-系列文章目录 调试笔记-20240713-Windows-Tauri 多个HTML页面支持 文章目录 调试笔记-系列文章目录调试笔记-20240713-Windows-Tauri 多个HTML页面支持 前言一、调试环境操作系统&#xff1a;Windows 10 专业版调试环境调试目标 二、调试步骤搜索相似问题 三、应用场…

Python中的数据容器及其在大数据开发中的应用

在Python编程中&#xff0c;数据容器是存储和组织数据的基本工具。作为大数据开发者&#xff0c;了解并灵活运用各种容器类型对于高效处理大规模数据至关重要。今天&#xff0c;我们将从Set出发&#xff0c;探讨Python中的各种数据容器&#xff0c;以及它们在大数据处理中的应用…

Leetcode3200. 三角形的最大高度

Every day a Leetcode 题目来源&#xff1a;3200. 三角形的最大高度 解法1&#xff1a;模拟 枚举第一行是红色还是蓝色&#xff0c;再按题意模拟即可。 代码&#xff1a; /** lc appleetcode.cn id3200 langcpp** [3200] 三角形的最大高度*/// lc codestart class Solutio…

【 香橙派 AIpro评测】烧系统到运行并使用Jupyter Lab 界面体验 AI 应用样例(新手福音)

文章目录 ⭐前言⭐初始化开发板⭐下载镜像烧系统⭐开发板初始化系统&#x1f496; 远程ssh&#x1f496;查看ubuntu桌面&#x1f496; 远程向日葵 ⭐体验 AI 应用样例&#x1f496; 运行 jupyterLab&#x1f496; 打开Jupyter Lab页面&#x1f496; 释放内存&#x1f496; 运行…

AI Native时代:重塑人机交互与创作流程

随着2024年上海世界人工智能大会的圆满落幕&#xff0c;业界领袖们纷纷就AI应用的新机遇展开深入讨论。结合a16z播客中的观点&#xff0c;本文将探讨AI原生&#xff08;AI Native&#xff09;应用的几个关键特征&#xff0c;这些特征正在重新定义我们的工作方式和创作过程。 一…

排序-java(详解)

一&#xff0c;分类 主要的排序大致分为以下几类&#xff1a; 1&#xff0c;插入排序&#xff0c;又分为直接插入排序和希尔排序 2&#xff0c;选择排序&#xff0c;又分为选择排序和堆排序 3&#xff0c;交换排序&#xff0c;又分为冒泡排序和快速排序 4&#xff0c;归并…

【学习笔记】无人机(UAV)在3GPP系统中的增强支持(三)-机上无线电接入节点无人机

引言 本文是3GPP TR 22.829 V17.1.0技术报告&#xff0c;专注于无人机&#xff08;UAV&#xff09;在3GPP系统中的增强支持。文章提出了多个无人机应用场景&#xff0c;分析了相应的能力要求&#xff0c;并建议了新的服务级别要求和关键性能指标&#xff08;KPIs&#xff09;。…

大模型高效参数微调技术

文章目录 一、Fine-Tuning&#xff1a;微调二、Prompt-Tuning&#xff1a;提示调优2.1 工作原理2.2 PET (Pattern-Exploiting Training)2.3 Prompt-Tuning集成2.4 模板构建方式 三、Prefix Tuning&#xff1a;连续提示模板3.1 提出动机3.2 工作原理 四、P-Tuning V1/V24.1 P-Tu…

【Qt课设】基于Qt实现的中国象棋

一、摘 要 本报告讨论了中国象棋程序设计的关键技术和方法。首先介绍了中国象棋的棋盘制作&#xff0c;利用Qt中的一些绘画类的函数来进行绘制。在创作中国象棋棋子方面&#xff0c;首先&#xff0c;我们先定义一下棋子类&#xff0c;将棋子中相同的部分进行打包&#xff0c;使…

redisTemplate报错为nil,通过redis-cli查看前缀有乱码

public void set(String key, String value, long timeout) {redisTemplate.opsForValue().set(key, value, timeout, TimeUnit.SECONDS);} 改完之后 public void set(String key, String value, long timeout) {redisTemplate.setKeySerializer(new StringRedisSerializer()…

前端工程化10-webpack静态的模块化打包工具之各种loader处理器

9.1、案例编写 我们创建一个component.js 通过JavaScript创建了一个元素&#xff0c;并且希望给它设置一些样式&#xff1b; 我们自己写的css,要把他加入到Webpack的图结构当中&#xff0c;这样才能被webpack检测到进行打包&#xff0c; style.css–>div_cn.js–>main…

【架构】分布式与微服务架构解析

分布式与微服务架构解析 一、分布式1、什么是分布式架构2、为什么需要分布式架构3、分布式架构有哪些优势&#xff1f;4、分布式架构有什么劣势&#xff1f;5、分布式架构有哪些关键技术&#xff1f;6、基于分布式架构如何提高其高性能&#xff1f;7、如何基于架构提高系统的稳…