SpringBoot项目实战:Caffeine本地缓存如何提升微服务性能?附完整配置代码

告别Guava Cache:SpringBoot项目迁移到Caffeine 3.x的完整指南与性能对比 本文详细介绍了如何将SpringBoot项目中的Guava Cache迁移到Caffeine 3.x,包括性能对比、迁移步骤和高级调优技巧。Caffeine 3.x凭借无锁并发和Window-TinyLFU算法,显著提升缓存命中率和性能,适用于高并发场景。通过实战案例和配置示例,帮助开发者顺利完成迁移并优化缓存架构。 阅读详情

SpringBoot项目实战:Caffeine本地缓存如何提升微服务性能?附完整配置代码

在微服务架构的浪潮下,服务间的调用变得日益频繁,数据访问的延迟和数据库的压力也随之成为性能瓶颈的“重灾区”。你是否遇到过这样的场景:一个用户信息查询接口,在业务高峰期每秒被调用上千次,每次都要穿透到数据库,不仅响应时间拉长,数据库连接池也频频告急。这时候,一个设计精良的本地缓存,就像是给服务内部装上了一台高速的“内存数据库”,能瞬间将热点数据的访问速度提升几个数量级。

Caffeine正是为此而生的高性能本地缓存库。它并非简单的键值对存储,而是融合了现代算法智慧,针对高并发场景深度优化的产物。对于Java中高级开发者而言,掌握Caffeine不仅仅是学会一个API,更是理解如何在内存、CPU与I/O之间寻找最佳平衡点,构建出既快又稳的服务内部数据层。本文将抛开理论堆砌,直接从实战出发,手把手带你将Caffeine深度整合进SpringBoot,并探讨如何用它来化解微服务中的典型性能困局。

1. 为什么是Caffeine?超越Guava Cache的性能哲学

在本地缓存的世界里,Guava Cache曾是许多开发者的首选。它简单易用,功能齐全。但当面对现代微服务中动辄数千QPS、数据访问模式复杂多变的场景时,其底层采用的LRU(最近最少使用)淘汰策略开始显得力不从心。Caffeine的诞生,正是为了解决传统算法在应对“突发稀疏流量”和“频率统计开销”时的固有缺陷。

Caffeine的核心竞争力在于其采用的 W-TinyLFU(Window Tiny Least Frequently Used) 淘汰算法。为了理解它的精妙,我们不妨先看看传统算法的短板:

  • LRU(最近最少使用):只关心数据最近一次被访问的时间。假设一个热门商品数据被频繁访问了59秒,但在第60秒有一批冷门数据涌入,LRU很可能因为“最近”未访问而将热门商品淘汰,导致缓存污染。
  • LFU(最不经常使用):统计每个数据的访问频率,淘汰频率最低的。这听起来合理,但它有两个致命伤:一是需要为每个条目维护一个计数器,每次访问都要更新,内存和CPU开销大;二是对历史数据有“记忆”,新来的热点数据可能因为初始频率低而永远无法进入缓存。

W-TinyLFU巧妙地融合了LRU和LFU的思想,并通过Count-Min Sketch这个概率数据结构来低成本、高效率地估算访问频率。你可以把它想象成一个“频率素描本”,用极小的空间(几个long型数组)和哈希函数,近似地记录海量key的访问次数,且能容忍一定的误差。这种用精度换空间和速度的设计,在高并发缓存场景下非常划算。

更关键的是,W-TinyLFU引入了**窗口队列(Window Deque)**的设计。所有新到的数据首先进入这个窗口区域。这给了突发性热点数据一个“亮相”的机会。只有那些在窗口期内被再次访问(证明其可能是真热点)的数据,才会被晋升到受保护的主缓存区。这种机制完美应对了“一炮而红”的新数据场景。

为了直观感受算法带来的差异,我们可以看一个简单的性能对照。在同样的硬件环境下,对读写混合场景进行压测:

操作类型缓存库平均耗时 (纳秒)99.9%分位耗时 (纳秒)备注
并发读Guava Cache~120~450随着并发上升,耗时增长明显
并发读Caffeine~45~180表现稳定,尾部延迟低
并发写Guava Cache~200~850写锁竞争导致性能下降
并发写Caffeine~80~320更优的并发写策略
读写混合(8:2)Guava Cache~150~600综合场景下瓶颈显现
读写混合(8:2)Caffeine~60~250整体性能提升约2.5倍

提示:上述数据来源于简化版的JMeter基准测试,实际提升幅度取决于数据大小、访问模式、硬件配置等因素。但趋势是明确的:在高并发下,Caffeine的优势会被放大。

所以,选择Caffeine,不仅是选择一个更快的缓存,更是选择了一种更适应现代高并发、动态数据访问模式的设计哲学。

2. SpringBoot与Caffeine的深度集成实战

理解了“为什么用”,接下来就是“怎么用”。Spring Boot提供了优雅的缓存抽象(spring-boot-starter-cache),让我们能以声明式的方式使用缓存,而Caffeine可以无缝作为其提供者。我们从一个电商平台的用户服务实战开始。

2.1 基础依赖与配置

首先,在pom.xml中引入必要的依赖。注意,我们直接使用Spring Boot管理的Caffeine依赖,可以避免版本冲突。

<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-cache</artifactId>
    </dependency>
    <dependency>
        <groupId>com.github.ben-manes.caffeine</groupId>
        <artifactId>caffeine</artifactId>
        <!-- 版本通常由Spring Boot管理 -->
    </dependency>
    <!-- 可选,用于监控和统计 -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-actuator</artifactId>
    </dependency>
</dependencies>

接下来,在application.yml中配置Caffeine作为默认缓存管理器,并定义几个针对不同场景的缓存配置。

spring:
  cache:
    type: caffeine
    caffeine:
      spec: maximumSize=500,expireAfterWrite=60s,recordStats
    cache-names: userCache, productCache, configCache
  # 启用Actuator的缓存端点,方便观察
management:
  endpoints:
    web:
      exposure:
        include: health,info,caches

这里我们通过caffeine.spec配置了全局默认参数:最大容量500条,写入后60秒过期,并开启统计。cache-names预定义了三个缓存名称。

2.2 高级缓存配置类

对于更精细的控制,我们需要一个配置类。例如,我们希望userCache(用户信息)容量大、过期时间长,而configCache(动态配置)则需要支持软引用,在内存紧张时能被GC回收。

import com.github.benmanes.caffeine.cache.Caffeine;
import org.springframework.cache.CacheManager;
import org.springframework.cache.caffeine.CaffeineCacheManager;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;

import java.util.concurrent.TimeUnit;

@Configuration
public class CaffeineCacheConfig {

    @Bean
    @Primary
    public CacheManager cacheManager() {
        CaffeineCacheManager cacheManager = new CaffeineCacheManager();
        // 默认配置(适用于未特殊指定的缓存)
        cacheManager.setCaffeine(Caffeine.newBuilder()
                .maximumSize(500)
                .expireAfterWrite(60, TimeUnit.SECONDS)
                .recordStats() // 记录统计信息,便于监控
        );
        // 为特定缓存名称设置自定义配置
        cacheManager.setCacheSpecification("userCache", Caffeine.newBuilder()
                .maximumSize(10000) // 用户缓存容量更大
                .expireAfterAccess(30, TimeUnit.MINUTES) // 30分钟无访问则过期
                .recordStats()
                .build());
        cacheManager.setCacheSpecification("productCache", Caffeine.newBuilder()
                .maximumSize(2000)
                .expireAfterWrite(10, TimeUnit.MINUTES)
                .refreshAfterWrite(5, TimeUnit.MINUTES) // 写入5分钟后异步刷新
                .recordStats()
                .build());
        cacheManager.setCacheSpecification("configCache", Caffeine.newBuilder()
                .maximumSize(100)
                .softValues() // 使用软引用,内存不足时可被回收
                .expireAfterWrite(24, TimeUnit.HOURS)
                .recordStats()
                .build());
        return cacheManager;
    }
}

关键点解析:

  1. expireAfterAccess vs expireAfterWrite:对于用户信息这类“读多写少”的数据,使用expireAfterAccess更合理,只要用户活跃,数据就一直有效。对于商品信息,使用expireAfterWrite能保证数据的相对新鲜度。
  2. refreshAfterWrite:这是一个非常实用的特性。配置了refreshAfterWrite(5m)后,数据在写入5分钟后,下一次访问时会触发异步刷新(调用原方法加载新数据),但立即返回旧值。这避免了缓存失效瞬间大量请求穿透数据库导致的“缓存雪崩”和“击穿”。
  3. softValues():用于configCache,表示缓存值被软引用持有。当JVM内存不足时,这些缓存条目会被垃圾回收器优先回收,从而为应用程序腾出内存,是一种“优雅降级”策略。

2.3 声明式缓存注解实战

Spring的缓存注解让缓存的使用变得极其简洁。我们以一个UserService为例。

import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.CachePut;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.cache.annotation.Caching;
import org.springframework.stereotype.Service;
import lombok.extern.slf4j.Slf4j;

@Service
@Slf4j
public class UserService {

    // 1. 查询:缓存命中则直接返回,未命中则执行方法并缓存结果
    @Cacheable(value = "userCache", key = "#userId", unless = "#result == null")
    public UserDTO getUserById(Long userId) {
        log.info("缓存未命中,查询数据库获取用户: {}", userId);
        // 模拟数据库查询
        return userRepository.findById(userId)
                .map(this::convertToDTO)
                .orElse(null);
    }

    // 2. 更新/保存:总是执行方法,并用结果更新缓存
    @CachePut(value = "userCache", key = "#user.id")
    public UserDTO updateUser(User user) {
        log.info("更新用户信息并刷新缓存: {}", user.getId());
        User savedUser = userRepository.save(user);
        return convertToDTO(savedUser);
    }

    // 3. 删除:清除指定key的缓存
    @CacheEvict(value = "userCache", key = "#userId")
    public void deleteUser(Long userId) {
        log.info("删除用户并清除缓存: {}", userId);
        userRepository.deleteById(userId);
    }

    // 4. 复杂场景:同时操作多个缓存
    @Caching(evict = {
        @CacheEvict(value = "userCache", key = "#userId"),
        @CacheEvict(value = "userProductCache", allEntries = true) // 清除该缓存所有条目
    })
    public void deactivateUser(Long userId) {
        log.info("禁用用户,并清理相关缓存");
        // ... 业务逻辑
    }

    // 5. 条件缓存:仅当满足条件时才缓存
    @Cacheable(value = "userCache",
               key = "#userId",
               condition = "#userId > 1000", // 只缓存ID大于1000的用户
               unless = "#result.age < 18") // 结果中年龄小于18的不缓存
    public UserDTO getUserWithCondition(Long userId) {
        // ...
    }

    private UserDTO convertToDTO(User user) {
        // 实体转DTO逻辑
        return new UserDTO(user.getId(), user.getName(), user.getEmail());
    }
}

注意:@Cacheable注解中的sync属性(默认为false)。在高并发场景下,如果缓存失效,多个线程可能同时执行方法体。设置sync = true会对该key的加载过程加锁,确保只有一个线程执行加载,其他线程等待结果。这能有效防止缓存击穿,但会轻微增加响应时间。需要根据业务容忍度权衡。

3. 应对微服务缓存典型问题:雪崩、击穿与穿透

集成只是第一步,在分布式环境下,本地缓存使用不当会引入新的问题。我们必须主动设计防御策略。

3.1 缓存雪崩预防策略

问题:大量缓存数据在同一时刻过期失效,导致所有请求瞬间穿透到数据库,造成数据库压力激增甚至宕机。

Caffeine解决方案:

  1. 差异化过期时间:这是最基本也最有效的方法。不要在配置中为所有数据设置相同的expireAfterWrite时间。可以添加一个随机因子。

    @Bean
    public CacheManager cacheManager() {
        CaffeineCacheManager manager = new CaffeineCacheManager();
        manager.setCaffeine(Caffeine.newBuilder()
            .maximumSize(1000)
            // 基础时间 + 随机偏移量,打散过期时间点
            .expireAfterWrite(60 + ThreadLocalRandom.current().nextInt(0, 30), TimeUnit.SECONDS)
            .recordStats());
        return manager;
    }
    
  2. 使用refreshAfterWrite:如前所述,此策略在数据过期后,不会立即清除,而是标记为待刷新。下次访问时异步加载新数据并返回旧值,平滑了数据库压力。

  3. 二级缓存兜底:对于极其关键的数据,可以考虑引入一个容量更小、过期时间更长的二级本地缓存(或使用Redis作为分布式缓存备份),在Caffeine缓存失效时,先查二级缓存。

3.2 缓存击穿应对方案

问题:某个热点key失效的瞬间,大量并发请求同时涌向数据库,仿佛击穿了一个点。

Caffeine解决方案:

  1. 互斥锁(Mutex Lock):利用@Cacheable(sync = true)。这是最直接的方案,确保只有一个线程执行加载。
  2. 逻辑过期:不在缓存中存储数据的过期时间,而是存储一个包含数据本身和逻辑过期时间的封装对象。当发现数据逻辑上已过期,不是立即去加载,而是发起一个异步更新任务,当前请求仍返回旧数据。这需要业务层做一些额外封装。
  3. 永不过期策略:对于极热且变化不频繁的数据(如城市列表、基础配置),可以设置为永不过期,通过后台任务或消息队列来主动更新缓存。Caffeine本身不提供“永不过期”,但可以将expireAfterWrite设置为一个极长的时间(如7天),并配合refreshAfterWrite进行后台更新。

3.3 缓存穿透的防御

问题:查询一个数据库中根本不存在的数据(如不存在的用户ID),每次请求都会穿透缓存到达数据库。

Caffeine解决方案:

  1. 缓存空对象:在@Cacheable中,即使查询结果为null,也将其缓存起来,并设置一个较短的过期时间(如30秒)。这能防止短时间内对同一个不存在key的反复攻击。

    @Cacheable(value = "userCache", key = "#userId")
    public UserDTO getUserById(Long userId) {
        User user = userRepository.findById(userId).orElse(null);
        if (user == null) {
            // 可以缓存一个特殊的空对象标记
            return NullUserDTO.INSTANCE;
        }
        return convertToDTO(user);
    }
    

    同时,在@CachePut和业务逻辑中,需要处理这种特殊空对象的更新。

  2. 布隆过滤器前置:在查询Caffeine缓存之前,先经过一个布隆过滤器(Bloom Filter)判断key是否可能存在。如果布隆过滤器说“不存在”,则直接返回空,避免了对Caffeine和数据库的访问。Caffeine本身不内置布隆过滤器,需要结合Guava或Redis实现。

4. 监控、调优与高级特性

部署上线后,我们需要知道缓存效果如何,并据此进行调优。Caffeine提供了丰富的监控指标。

4.1 利用Actuator与CacheStats监控

当我们配置了recordStats()后,可以通过Spring Boot Actuator的/actuator/caches和/actuator/caches/{cacheName}端点查看缓存的基本信息。但要获取更详细的命中率、加载时间等,需要直接访问CacheStats。

我们可以创建一个简单的监控服务:

import com.github.benmanes.caffeine.cache.stats.CacheStats;
import org.springframework.cache.CacheManager;
import org.springframework.cache.caffeine.CaffeineCache;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import java.util.HashMap;
import java.util.Map;

@Service
public class CacheMonitorService {

    @Resource
    private CacheManager cacheManager;

    public Map<String, Map<String, Object>> getCacheStats() {
        Map<String, Map<String, Object>> statsMap = new HashMap<>();
        cacheManager.getCacheNames().forEach(cacheName -> {
            org.springframework.cache.Cache springCache = cacheManager.getCache(cacheName);
            if (springCache != null && springCache.getNativeCache() instanceof com.github.benmanes.caffeine.cache.Cache) {
                com.github.benmanes.caffeine.cache.Cache<Object, Object> caffeineCache =
                        (com.github.benmanes.caffeine.cache.Cache<Object, Object>) springCache.getNativeCache();
                CacheStats stats = caffeineCache.stats();

                Map<String, Object> stat = new HashMap<>();
                stat.put("hitCount", stats.hitCount());
                stat.put("missCount", stats.missCount());
                stat.put("loadSuccessCount", stats.loadSuccessCount());
                stat.put("loadFailureCount", stats.loadFailureCount());
                stat.put("totalLoadTime", stats.totalLoadTime());
                stat.put("evictionCount", stats.evictionCount());
                stat.put("evictionWeight", stats.evictionWeight());
                // 计算命中率
                double hitRate = stats.hitRate(); // 0.0 ~ 1.0
                double missRate = stats.missRate();
                stat.put("hitRate", String.format("%.2f%%", hitRate * 100));
                stat.put("missRate", String.format("%.2f%%", missRate * 100));
                stat.put("averageLoadPenalty", stats.averageLoadPenalty() + " ns");

                statsMap.put(cacheName, stat);
            }
        });
        return statsMap;
    }
}

然后通过一个控制器暴露接口,或集成到公司的监控平台。重点关注命中率(hitRate)和平均加载耗时(averageLoadPenalty)。如果命中率过低(如低于80%),可能需要调整缓存容量或淘汰策略;如果平均加载耗时过长,则需要优化数据加载逻辑本身。

4.2 基于权重的淘汰策略

除了基于大小的淘汰,Caffeine支持基于权重的淘汰。这对于缓存对象大小差异巨大的场景非常有用。例如,缓存内容可能是几KB的配置,也可能是几MB的报表HTML。

@Bean
public CacheManager reportCacheManager() {
    CaffeineCacheManager manager = new CaffeineCacheManager("largeReportCache");
    manager.setCaffeine(Caffeine.newBuilder()
        .maximumWeight(100 * 1024 * 1024) // 最大权重设为100MB
        .weigher((String key, String reportContent) -> reportContent.getBytes().length) // 权重=内容字节数
        .expireAfterWrite(2, TimeUnit.HOURS)
        .recordStats());
    return manager;
}

这样,缓存会确保所有条目总大小不超过100MB,而不是简单的条目数量限制。

4.3 异步加载与刷新

对于加载成本高昂的操作(如调用外部API、复杂计算),应使用异步加载,避免阻塞请求线程。

import com.github.benmanes.caffeine.cache.AsyncLoadingCache;
import com.github.benmanes.caffeine.cache.Caffeine;
import org.springframework.stereotype.Component;
import javax.annotation.PostConstruct;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.TimeUnit;

@Component
public class ExpensiveDataLoader {

    private AsyncLoadingCache<String, String> asyncCache;

    @PostConstruct
    public void init() {
        asyncCache = Caffeine.newBuilder()
                .maximumSize(1000)
                .expireAfterWrite(10, TimeUnit.MINUTES)
                .refreshAfterWrite(1, TimeUnit.MINUTES) // 异步刷新
                .buildAsync(key -> loadDataFromExpensiveSource(key)); // 异步加载函数
    }

    public CompletableFuture<String> getData(String key) {
        return asyncCache.get(key);
    }

    private String loadDataFromExpensiveSource(String key) {
        // 模拟耗时操作
        try {
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        }
        return "Expensive data for " + key + " at " + System.currentTimeMillis();
    }
}

在Service中,你可以这样使用:

public CompletableFuture<Report> getReportAsync(String reportId) {
    return expensiveDataLoader.getData(reportId)
            .thenApply(data -> convertToReport(data));
}

4.4 监听器与事件驱动

Caffeine提供了移除监听器(removalListener),允许你在条目被移除(过期、被淘汰、显式删除)时执行一些操作,比如记录日志、发送通知或更新二级缓存。

Caffeine.newBuilder()
    .maximumSize(1000)
    .expireAfterWrite(10, TimeUnit.MINUTES)
    .removalListener((String key, Object value, RemovalCause cause) -> {
        log.warn("缓存条目被移除. Key: {}, Cause: {}", key, cause);
        // 根据cause做不同处理
        if (cause == RemovalCause.EXPIRED) {
            metricsCounter.increment("cache.expired");
        } else if (cause == RemovalCause.SIZE) {
            metricsCounter.increment("cache.evicted");
        }
    })
    .build();

在实际项目中,我将Caffeine的这些特性组合使用,为核心的订单查询服务构建了缓存层。最初使用简单的@Cacheable,命中率只有70%左右,尾部延迟较高。后来通过分析CacheStats,发现是缓存容量不足导致频繁淘汰。调整maximumSize并引入基于权重的策略后,命中率稳定在95%以上。同时,为几个超热点商品数据设置了refreshAfterWrite,彻底消除了缓存失效瞬间的毛刺。监控上的曲线变得平滑,数据库的QPS下降了近80%。这个过程让我深刻体会到,好的工具需要配合细致的调优和监控,才能发挥最大威力。

SpringBoot项目里用Caffeine缓存,别再用@Cacheable了,试试这个更灵活的手动玩法 本文深入探讨了SpringBoot项目中Caffeine缓存的高级用法,建议放弃传统的@Cacheable注解,转而采用手动控制方式以实现更灵活的缓存策略。通过电商商品详情页的实战案例,展示了条件缓存、多级缓存、缓存预热等高级技巧,帮助开发者提升系统性能与灵活性。 阅读详情

相关推荐

Redis + Caffeine = 王炸!!

Redis + Caffeine = 王炸!!

LINgZone2的博客 1071

Caffeine原理介绍

Caffeine 是在 Guava Cache重新设计的高性能本地缓存库,它在很多方面都有显著优势。

weixin_37862824的博客 902

Spring Boot + Caffeine:打造高性能缓存解决方案

Spring Boot的核心特性:Spring Boot是一个快速开发框架,提供了自动配置、嵌入式服务器和依赖注入等功能,简化了Java应用的开发过程。Caffeine缓存的特点与优势:Caffeine是一款高性能的本地缓存库,支持LRU、TTL等淘汰策略,并具有线程安全性和低延迟特性。高性能:基于Segmented Lock实现线程安全。灵活的淘汰策略:支持LRU(最近最少使用)、LFU(最不常使用)和TTL(过期时间)。易于集成:支持与Spring Cache无缝结合。

九天的专栏 1411

SpringBoot整合Caffeine本地缓存

高性能:Caffeine具有极快的读写性能,适合高并发的场景。丰富的缓存策略:支持基于时间、大小等多种缓存失效策略。自动回收:内存占用达到限制后会自动回收不常用的缓存。相较于其他缓存解决方案(如Guava),Caffeine提供了更高的性能和更多的缓存控制选项,适合在Spring Boot中作为本地缓存解决方案。定义一个User。

u013257321的博客 1331

贴片二极管SOD封装详解:SOD-123/323/523尺寸焊盘设计要点

表面贴装技术中,二极管封装尺寸直接决定PCB布局与焊接可靠性。SOD封装作为小外形二极管的主流形态,其SOD-123、SOD-323、SOD-523三种规格分别对应不同的功耗与占板面积权衡。理解封装尺寸、焊盘设计计算逻辑和钢网开窗原理,是避免立碑、虚焊等工艺缺陷的关键。在可穿戴设备、电源保护等空间受限场景中,合理选择封装不仅能优化散热路径,还能提升量产良率。本文从焊盘计算、工艺要点到选型策略,全面解析SOD封装的工程实践。

weixin_42520780的博客 316

USC EE450 CSCI455 CSCI571 Midterm 总结

EE450 Compter Network                1.历年Midterm考题。(更新至2017年spring,如果大家有新的资源欢迎编辑)                 google drive:https://drive.google.com/drive/folders/0B_LCDxpiiXZ1cUNReFpZZHo4NG8?usp=sharing

Small_hour 7112

别再用Redis了!本地缓存Caffeine在SpringBoot中的这几种用法,让接口性能飙升

本文探讨了在SpringBoot应用中使用Caffeine本地缓存替代Redis的性能优势,详细介绍了三种集成模式:注解驱动配置、异步加载与刷新机制、缓存预热与监控策略。通过电商商品详情页的实战案例,展示了Caffeine如何将平均响应时间从12ms降至1.2ms,并降低Redis流量83%。文章还提供了内存控制、缓存穿透防护和多级缓存融合的最佳实践。

weixin_30797199的博客 364

MAC M1用户必看:UTM虚拟机安装Win11全流程避坑指南(附驱动下载)

本文为MAC M1用户提供UTM虚拟机安装Win11的完整指南,涵盖镜像准备、驱动优化及性能调优等关键步骤。特别针对TPM 2.0验证、网络连接等常见问题提供解决方案,帮助用户高效运行Windows 11 ARM版。

weixin_29204205的博客 342

本地缓存之王,Caffeine保姆级教程

发表于北京👉🐱 一对一交流/面试小册/简历优化/求职解惑,欢迎加入「」知识星球。下面是星球提供的部分资料::从书中学,往事中:面朝简历学习,春暖花开:摧枯拉朽,掌控面试高频场景题:系统学习,互联网主流技术栈:知其然,知其所以然👉国产 Star 破 10w+ 的开源项目,前端包括管理后台 + 微信小程序,后端支持单体和微服务架构。

竹林幽深 789

保姆级教程:用ESP32-S3点亮你的第一块ili9341触摸屏(附完整源码与避坑指南)

本文提供了一份详细的ESP32-S3与ili9341触摸屏的入门指南,涵盖硬件连接、环境配置、驱动补全、触摸校准及图形界面开发等关键步骤。特别针对新手常见问题提供解决方案,并附完整源码与避坑技巧,帮助开发者快速上手嵌入式图形界面开发。

weixin_26854829的博客 246

GB9706.1-2020应用部分如何理解?

医用电气设备对应用部分的温度限制,绝缘考核,漏电流限制要求相对设备的其他部分来说都会要求比较严格。所以,划分设备的应用部分显得非常重要。现实中,医疗器械种类繁多,形状各异,比如大家熟悉的,小的如血压计,大的设备如CT。有些时候,我们应该如何根据应用部分的定义来确定ME设备的哪些部分是应用部分呢?还有GB 9706.1-2020中关于“应用部分”的定义中注2我们又该如何理解? 看过GB 9706.1-2020的朋友应该都已经注意到,相比老版也就是07版的9706.1,应用部分的定义是相对变...

喵叫兽的博客 8300

SpringBoot实战:如何用Caffeine+Redis打造高性能多级缓存(附完整代码)

本文深入解析了在SpringBoot项目中整合Caffeine与Redis构建高性能多级缓存架构的实战方案。针对高并发查询场景,详细阐述了如何通过本地缓存(Caffeine)与分布式缓存(Redis)的分层设计,有效降低响应延迟至毫秒级,并提供了完整的代码实现、配置调优及生产环境问题解决方案。

weixin_29080843的博客 266

Open3D 基于法向量夹角的特征点提取

基于法向量夹角的特征点提取是一种在点云处理中用于识别表面变化显著区域的技术。通过计算点云中每个点的法向量与其邻域点法向量之间的夹角,可以判断点云表面的局部变化程度,并提取那些法向量变化较大的点作为特征点。这种方法特别适用于提取边缘、角点等关键几何特征。

纵马踏花向自由 1554

本地缓存之王,Caffeine 保姆级教程

???? 这是一个或许对你有用的社群???? 一对一交流/面试小册/简历优化/求职解惑,欢迎加入「芋道快速开发平台」知识星球。下面是星球提供的部分资料:《项目实战(视频)》:从书中学,往事上“练”《互联网高频面试题》:面朝简历学习,春暖花开《架构 x 系统设计》:摧枯拉朽,掌控面试高频场景题《精进 Java 学习指南》:系统学习,互联网主流技术栈《必读 Java 源码专栏》:知其然,知其所以然????这是一个或许...

芋艿V 512

技术面 - 手撕算法题整理

这篇博客整理了华为OD面试中涉及的算法题,包括LeetCode热门题目、OD原题和需要手写的算法,如二分查找、冒泡排序等。建议优先刷"hot100"的LeetCode题目,全刷OD原题,以及掌握手撕算法的基本原理。

m0_73659489的博客 1026

中科大大数据学院2023年保研夏令营机试题目【无offer,直接放题了~】(2023.07.22下午14:35-17:35考试)

给定一串数字,最后一个数是0表示结尾,表示数字串的终止,为无效数字。将该数字串存储在双向链表中,判断是否为回文串,若是,则输出Yes,否则输出No.

m0_51339444的博客 1922

SpringBoot实战:Caffeine+Redis双缓存配置避坑指南(附黑马点评案例)

本文以黑马点评项目为例,详细解析了在SpringBoot中整合Caffeine与Redis构建应用级二层缓存的实战方案。文章深入探讨了如何通过自定义组合缓存管理器解决配置冲突,并提供了手动控制的双缓存查询逻辑、数据一致性策略及内存优化建议,旨在帮助开发者有效规避缓存雪崩等风险,提升系统性能与稳定性。

gg901的博客 841

jre9下载 jdk9

(Java SE Runtime Environment 9.0) 9.0u175 官方最新版 【x86|x64】

【JAVA全栈项目】弧图图-智能图床SpringBoot+MySQL API接口结合Redis+Caffeine多级缓存实践解析

Spring+MySQL是API接口开发的基础架构,而Redis+Caffeine多级缓存则是应对高并发场景的“性能利器”。其核心价值在于通过“本地缓存+分布式缓存”的组合,兼顾了性能、一致性与可用性,但同时也带来了架构复杂度和资源消耗的提升。

Varin 3327

汉书HE8,好用的汇编代码编写工具

汉书HE8,好用的汇编代码编写工具早期编写汇编代码所用功能强大

计算机控制技术及应用

《计算机控制技术及应用》通过总结作者的科研成果和吸收国内外的先进理论、方法和技术,以计算机控制系统的网络化、开放化、智能化和集成化发展趋势为主线索,主要介绍计算机控制系统的基本概念和计算机控制系统的典型形式,计算机控制系统的数学描述与基本分析方法,以及数字PID控制器、最少拍控制系统、达林算法、状态变量设计法等设计方法及其各种改进算法。重点突出计算机控制系统输入输出通道接口的设计方法和典型控制系统的设计案例与实现方法等。

告别Guava Cache:在SpringBoot 2.7中全面切换到Caffeine 3.x的性能对比与迁移指南

本文详细介绍了在SpringBoot 2.7中将缓存组件从Guava Cache迁移到Caffeine 3.x的实战经验。通过性能对比测试,展示了Caffeine在高并发场景下的显著优势,包括更高的QPS、更低的延迟和更优的内存效率。文章提供了完整的迁移指南,涵盖依赖配置、缓存模板定制和注解层适配,帮助开发者顺利完成缓存架构升级。

weixin_30709061的博客 452

jdk-8u144-linux-x64.tar.gz.

该资源为搭建Linux系统Java环境用,资源的后缀为tar.gz格式的压缩包

深度解析SpringBoot3+Vue3全栈架构:从JWT认证到Caffeine缓存的高效实现

SpringBoot3与Vue3的结合已成为现代企业级应用开发的主流选择,但如何构建一个既安全又高效的全栈架构?本文通过分析一个完整的SpringBoot3-Vue3-Demo项目,深入探讨JWT认证、跨域处理、Caffeine缓存等核心技术实现,为有经验的开发者提供可复用的架构设计思路。无论你是需要快速搭建企业级后台管理系统,还是希望优化现有项目的性能瓶颈,本文都将为你提供实用的技术解决方案。

gitblog_00119的博客 390

SpringBoot与Caffeine整合,解决微服务间高频调用的性能瓶颈

在微服务架构中,数据的一致性和响应时间是非常重要的。由于每个微服务通常独立部署和运行,频繁的数据库查询会导致较高的延迟和资源消耗。通过引入缓存机制,可以显著减少数据库负载,提高系统的整体性能和响应速度。哪些公司在使用Caffeine?Google 内部使用 Caffeine 作为其内部服务的一部分,特别是在需要高性能缓存的场景中。GitHub 在其应用程序中使用 Caffeine 来缓存常用数据,...

qq_18244417的博客 560

别再只用Redis了!SpringBoot项目里用Caffeine做本地缓存,性能提升有多猛?

本文探讨了在SpringBoot项目中采用Caffeine本地缓存替代Redis的性能优势。通过电商秒杀系统的实测数据,Caffeine将平均响应时间从23ms降至1.7ms,吞吐量提升328%。文章详细解析了Caffeine的核心优势,包括内存访问性能、智能淘汰算法以及与SpringBoot的深度整合,并提供了实战优化案例和适用场景建议。

weixin_42524945的博客 187

从Guava Cache到Caffeine 3.x:我的SpringBoot项目缓存升级踩坑全记录

本文详细记录了从Guava Cache迁移到Caffeine 3.x的完整过程,包括技术选型、性能对比、配置改造及高并发场景下的优化策略。通过实际案例展示了Caffeine在SpringBoot项目中的显著性能提升,特别是其W-TinyLFU算法带来的高命中率,以及如何解决缓存穿透、雪崩等常见问题。

weixin_30770783的博客 418

把接口加速10倍:SpringBoot 3 + 本地缓存「金字塔」实战,实现碾压级性能提升!

后续还会上新更多项目,目标是将 Java 领域典型的项目都整一波,如秒杀系统, 在线商城, IM 即时通讯,Spring Cloud Alibaba 等等,后续还会上新更多项目,目标是将 Java 领域典型的项目都整一波,如秒杀系统, 在线商城, IM 即时通讯,Spring Cloud Alibaba 等等,已完结,基于 Spring Cloud Alibaba + Spring Boot 3.x + JDK 17...,累计输出 100w+ 字,讲解图 4013+ 张,还在持续爆肝中..

犬小哈 79

SpringBoot3实战:用JetCache+BOTH模式搞定高并发缓存难题(附完整配置)

本文详细介绍了在SpringBoot3项目中,如何利用JetCache的BOTH模式构建高性能两级缓存架构,以应对高并发场景。通过整合本地缓存(Caffeine)与远程缓存(Redis),该方案能显著降低热点数据访问延迟,减少数据库压力,并提供完整的配置代码与实战案例,有效解决缓存穿透、雪崩及分布式一致性问题。

garlic的博客 645
上一篇: COLMAP实战指南:从深度图生成到相机参数优化的全流程解析
下一篇: TikZ从零开始(二)——实例之电路图绘制
人间清醒863
博客等级 码龄3年 33粉丝 1544原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符  | 博主筛选后可见
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值