最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

SpringBoot啟動(dòng)時(shí)將數(shù)據(jù)庫(kù)數(shù)據(jù)預(yù)加載到Redis緩存的幾種實(shí)現(xiàn)方案

 更新時(shí)間:2025年09月29日 09:32:37   作者:塔中妖  
在實(shí)際項(xiàng)目開(kāi)發(fā)中,我們經(jīng)常需要在應(yīng)用啟動(dòng)時(shí)將一些固定的、頻繁訪問(wèn)的數(shù)據(jù)從數(shù)據(jù)庫(kù)預(yù)加載到 Redis 緩存中,以提高系統(tǒng)性能,本文將介紹幾種實(shí)現(xiàn)方案,需要的朋友可以參考下

引言

在實(shí)際項(xiàng)目開(kāi)發(fā)中,我們經(jīng)常需要在應(yīng)用啟動(dòng)時(shí)將一些固定的、頻繁訪問(wèn)的數(shù)據(jù)從數(shù)據(jù)庫(kù)預(yù)加載到 Redis 緩存中,以提高系統(tǒng)性能。本文將介紹幾種實(shí)現(xiàn)方案。

方案一:使用 @PostConstruct 注解

這是最簡(jiǎn)單直接的方式,在 Spring Bean 初始化完成后執(zhí)行預(yù)加載邏輯。

@Component
@Slf4j
public class CachePreloader {
    
    @Autowired
    private UserService userService;
    
    @Autowired
    private RedisTemplate<String, Object> redisTemplate;
    
    @PostConstruct
    public void preloadCache() {
        log.info("開(kāi)始預(yù)加載緩存數(shù)據(jù)...");
        
        try {
            // 加載用戶基礎(chǔ)信息
            List<User> users = userService.getAllActiveUsers();
            for (User user : users) {
                String key = "user:" + user.getId();
                redisTemplate.opsForValue().set(key, user, Duration.ofHours(24));
            }
            
            // 加載系統(tǒng)配置
            List<SystemConfig> configs = systemConfigService.getAllConfigs();
            for (SystemConfig config : configs) {
                String key = "config:" + config.getKey();
                redisTemplate.opsForValue().set(key, config.getValue(), Duration.ofDays(7));
            }
            
            log.info("緩存預(yù)加載完成,共加載 {} 條用戶數(shù)據(jù),{} 條配置數(shù)據(jù)", 
                    users.size(), configs.size());
                    
        } catch (Exception e) {
            log.error("緩存預(yù)加載失敗", e);
        }
    }
}

方案二:實(shí)現(xiàn) ApplicationRunner 接口

ApplicationRunner 在應(yīng)用啟動(dòng)完成后執(zhí)行,可以獲取命令行參數(shù),更適合復(fù)雜的初始化邏輯。

@Component
@Order(1) // 設(shè)置執(zhí)行順序
@Slf4j
public class CacheApplicationRunner implements ApplicationRunner {
    
    @Autowired
    private DataPreloadService dataPreloadService;
    
    @Override
    public void run(ApplicationArguments args) throws Exception {
        log.info("ApplicationRunner 開(kāi)始執(zhí)行緩存預(yù)加載...");
        
        // 檢查是否需要預(yù)加載
        if (args.containsOption("skip-cache-preload")) {
            log.info("跳過(guò)緩存預(yù)加載");
            return;
        }
        
        dataPreloadService.preloadAllCache();
        log.info("ApplicationRunner 緩存預(yù)加載完成");
    }
}

方案三:監(jiān)聽(tīng) ApplicationReadyEvent 事件

這種方式在應(yīng)用完全啟動(dòng)就緒后執(zhí)行,是最安全的預(yù)加載時(shí)機(jī)。

@Component
@Slf4j
public class CacheEventListener {
    
    @Autowired
    private RedisTemplate<String, Object> redisTemplate;
    
    @Autowired
    private ProductService productService;
    
    @EventListener
    public void handleApplicationReady(ApplicationReadyEvent event) {
        log.info("應(yīng)用啟動(dòng)完成,開(kāi)始預(yù)加載緩存...");
        
        // 異步執(zhí)行預(yù)加載,避免阻塞啟動(dòng)
        CompletableFuture.runAsync(() -> {
            try {
                preloadProductCache();
                preloadCategoryCache();
            } catch (Exception e) {
                log.error("異步預(yù)加載緩存失敗", e);
            }
        });
    }
    
    private void preloadProductCache() {
        List<Product> hotProducts = productService.getHotProducts();
        hotProducts.forEach(product -> {
            String key = "hot_product:" + product.getId();
            redisTemplate.opsForValue().set(key, product, Duration.ofHours(12));
        });
        log.info("熱門(mén)商品緩存預(yù)加載完成,共 {} 條", hotProducts.size());
    }
    
    private void preloadCategoryCache() {
        List<Category> categories = productService.getAllCategories();
        redisTemplate.opsForValue().set("all_categories", categories, Duration.ofDays(1));
        log.info("商品分類(lèi)緩存預(yù)加載完成");
    }
}

方案四:創(chuàng)建專門(mén)的預(yù)加載服務(wù)

將預(yù)加載邏輯封裝成獨(dú)立的服務(wù),便于管理和測(cè)試。

@Service
@Slf4j
public class DataPreloadService {
    
    @Autowired
    private RedisTemplate<String, Object> redisTemplate;
    
    @Autowired
    private DictService dictService;
    
    @Autowired
    private RegionService regionService;
    
    /**
     * 預(yù)加載所有緩存數(shù)據(jù)
     */
    public void preloadAllCache() {
        long startTime = System.currentTimeMillis();
        
        try {
            // 并行加載多種數(shù)據(jù)
            CompletableFuture<Void> dictFuture = CompletableFuture.runAsync(this::preloadDictCache);
            CompletableFuture<Void> regionFuture = CompletableFuture.runAsync(this::preloadRegionCache);
            
            // 等待所有任務(wù)完成
            CompletableFuture.allOf(dictFuture, regionFuture).join();
            
            long endTime = System.currentTimeMillis();
            log.info("所有緩存預(yù)加載完成,耗時(shí): {} ms", endTime - startTime);
            
        } catch (Exception e) {
            log.error("緩存預(yù)加載過(guò)程中發(fā)生異常", e);
        }
    }
    
    /**
     * 預(yù)加載數(shù)據(jù)字典
     */
    private void preloadDictCache() {
        try {
            Map<String, List<DictItem>> dictMap = dictService.getAllDictItems();
            dictMap.forEach((dictType, items) -> {
                String key = "dict:" + dictType;
                redisTemplate.opsForValue().set(key, items, Duration.ofDays(30));
            });
            log.info("數(shù)據(jù)字典緩存預(yù)加載完成,共 {} 種類(lèi)型", dictMap.size());
        } catch (Exception e) {
            log.error("數(shù)據(jù)字典緩存預(yù)加載失敗", e);
        }
    }
    
    /**
     * 預(yù)加載地區(qū)數(shù)據(jù)
     */
    private void preloadRegionCache() {
        try {
            List<Region> regions = regionService.getAllRegions();
            redisTemplate.opsForValue().set("all_regions", regions, Duration.ofDays(30));
            
            // 按層級(jí)緩存
            Map<Integer, List<Region>> regionsByLevel = regions.stream()
                .collect(Collectors.groupingBy(Region::getLevel));
            
            regionsByLevel.forEach((level, levelRegions) -> {
                String key = "regions_level:" + level;
                redisTemplate.opsForValue().set(key, levelRegions, Duration.ofDays(30));
            });
            
            log.info("地區(qū)數(shù)據(jù)緩存預(yù)加載完成,共 {} 條記錄", regions.size());
        } catch (Exception e) {
            log.error("地區(qū)數(shù)據(jù)緩存預(yù)加載失敗", e);
        }
    }
}

配置文件控制

application.yml 中添加配置項(xiàng),控制預(yù)加載行為:

app:
  cache:
    preload:
      enabled: true
      async: true
      timeout: 30000  # 超時(shí)時(shí)間(毫秒)
      batch-size: 1000  # 批處理大小

對(duì)應(yīng)的配置類(lèi):

@ConfigurationProperties(prefix = "app.cache.preload")
@Data
public class CachePreloadProperties {
    private boolean enabled = true;
    private boolean async = true;
    private long timeout = 30000;
    private int batchSize = 1000;
}

最佳實(shí)踐建議

1. 異常處理

預(yù)加載過(guò)程中的異常不應(yīng)該影響應(yīng)用啟動(dòng):

@PostConstruct
public void preloadCache() {
    try {
        // 預(yù)加載邏輯
    } catch (Exception e) {
        log.error("緩存預(yù)加載失敗,但不影響應(yīng)用啟動(dòng)", e);
        // 可以發(fā)送告警通知
    }
}

2. 分批處理

對(duì)于大量數(shù)據(jù),應(yīng)該分批處理避免內(nèi)存溢出:

private void preloadLargeDataset() {
    int pageSize = cachePreloadProperties.getBatchSize();
    int pageNum = 0;
    
    while (true) {
        List<DataEntity> dataList = dataService.getDataByPage(pageNum, pageSize);
        if (dataList.isEmpty()) {
            break;
        }
        
        // 批量寫(xiě)入 Redis
        dataList.forEach(data -> {
            String key = "data:" + data.getId();
            redisTemplate.opsForValue().set(key, data, Duration.ofHours(6));
        });
        
        pageNum++;
        log.info("已預(yù)加載第 {} 批數(shù)據(jù),本批 {} 條", pageNum, dataList.size());
    }
}

3. 監(jiān)控和告警

添加預(yù)加載狀態(tài)監(jiān)控:

@Component
public class CachePreloadMonitor {
    
    private final MeterRegistry meterRegistry;
    private final Counter preloadSuccessCounter;
    private final Counter preloadFailureCounter;
    
    public CachePreloadMonitor(MeterRegistry meterRegistry) {
        this.meterRegistry = meterRegistry;
        this.preloadSuccessCounter = Counter.builder("cache.preload.success").register(meterRegistry);
        this.preloadFailureCounter = Counter.builder("cache.preload.failure").register(meterRegistry);
    }
    
    public void recordSuccess(String cacheType, long count) {
        preloadSuccessCounter.increment();
        Gauge.builder("cache.preload.count")
            .tag("type", cacheType)
            .register(meterRegistry, count, Number::doubleValue);
    }
    
    public void recordFailure(String cacheType) {
        preloadFailureCounter.increment();
    }
}

總結(jié)

選擇合適的預(yù)加載方案需要考慮以下因素:

  • @PostConstruct: 適合簡(jiǎn)單的預(yù)加載邏輯
  • ApplicationRunner: 適合需要命令行參數(shù)的場(chǎng)景
  • ApplicationReadyEvent: 最安全的預(yù)加載時(shí)機(jī)
  • 專門(mén)的服務(wù): 適合復(fù)雜的預(yù)加載需求

無(wú)論選擇哪種方案,都要注意異常處理、性能優(yōu)化和監(jiān)控告警,確保預(yù)加載過(guò)程不會(huì)影響應(yīng)用的正常啟動(dòng)和運(yùn)行。

以上就是SpringBoot啟動(dòng)時(shí)將數(shù)據(jù)庫(kù)數(shù)據(jù)預(yù)加載到Redis緩存的幾種實(shí)現(xiàn)方案的詳細(xì)內(nèi)容,更多關(guān)于SpringBoot數(shù)據(jù)加載到Redis緩存的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • SpringBoot項(xiàng)目動(dòng)態(tài)指定配置文件的方法示例

    SpringBoot項(xiàng)目動(dòng)態(tài)指定配置文件的方法示例

    SpringBoot 作為 Java 開(kāi)發(fā)中一個(gè)非常流行的框架,以其簡(jiǎn)化配置和快速啟動(dòng)應(yīng)用而聞名,在開(kāi)發(fā)過(guò)程中,我們經(jīng)常需要根據(jù)不同的環(huán)境來(lái)加載不同的配置文件,本文給大家介紹了SpringBoot項(xiàng)目動(dòng)態(tài)指定配置文件的方法示例,需要的朋友可以參考下
    2024-12-12
  • Spring Boot配置管理最佳實(shí)踐方案

    Spring Boot配置管理最佳實(shí)踐方案

    本文將詳細(xì)介紹Spring Boot的配置管理相關(guān)內(nèi)容,包括配置優(yōu)先級(jí)、多環(huán)境管理、屬性注入、安全配置、配置校驗(yàn)和熱更新等主題,本文通過(guò)實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友參考下吧
    2026-02-02
  • JDBC編程的詳細(xì)步驟

    JDBC編程的詳細(xì)步驟

    這篇文章主要介紹了JDBC編程的詳細(xì)步驟,文中有非常詳細(xì)的代碼示例,對(duì)正在學(xué)習(xí)JDBC編程的小伙伴們有很好的幫助,需要的朋友可以參考下
    2021-05-05
  • 關(guān)于報(bào)錯(cuò)IDEA Terminated with exit code 1的解決方法

    關(guān)于報(bào)錯(cuò)IDEA Terminated with exit code 

    如果在IDEA構(gòu)建項(xiàng)目時(shí)遇到下面這樣的報(bào)錯(cuò)IDEA Terminated with exit code 1,那必然是Maven的設(shè)置參數(shù)重置了,導(dǎo)致下載錯(cuò)誤引起的,本文給大家分享兩種解決方法,需要的朋友可以參考下
    2022-08-08
  • springboot項(xiàng)目實(shí)現(xiàn)定時(shí)備份數(shù)據(jù)庫(kù)導(dǎo)出sql文件方式

    springboot項(xiàng)目實(shí)現(xiàn)定時(shí)備份數(shù)據(jù)庫(kù)導(dǎo)出sql文件方式

    這篇文章主要介紹了springboot項(xiàng)目實(shí)現(xiàn)定時(shí)備份數(shù)據(jù)庫(kù)導(dǎo)出sql文件方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2025-03-03
  • 詳解Spring Boot中整合Sharding-JDBC讀寫(xiě)分離示例

    詳解Spring Boot中整合Sharding-JDBC讀寫(xiě)分離示例

    這篇文章主要介紹了詳解Spring Boot中整合Sharding-JDBC讀寫(xiě)分離示例,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧
    2019-03-03
  • Java中Map的五種遍歷方式實(shí)現(xiàn)與對(duì)比

    Java中Map的五種遍歷方式實(shí)現(xiàn)與對(duì)比

    其實(shí)Map遍歷藏著多種玩法,有的優(yōu)雅簡(jiǎn)潔,有的性能拉滿,今天咱們盤(pán)一盤(pán)這些進(jìn)階偏基礎(chǔ)的遍歷方式,告別重復(fù)又臃腫的代碼,感興趣的小伙伴可以了解下
    2025-12-12
  • Idea配置超詳細(xì)圖文教程(2020.2版本)

    Idea配置超詳細(xì)圖文教程(2020.2版本)

    這篇文章主要介紹了Idea配置超詳細(xì)圖文教程(2020.2版本),本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2020-08-08
  • spring AOP定義AfterThrowing增加處理實(shí)例分析

    spring AOP定義AfterThrowing增加處理實(shí)例分析

    這篇文章主要介紹了spring AOP定義AfterThrowing增加處理,結(jié)合實(shí)例形式分析了spring面向切面AOP定義AfterThrowing相關(guān)實(shí)現(xiàn)步驟與操作技巧,需要的朋友可以參考下
    2020-01-01
  • mybatis-plus自動(dòng)填充插入更新時(shí)間有8小時(shí)時(shí)差

    mybatis-plus自動(dòng)填充插入更新時(shí)間有8小時(shí)時(shí)差

    本文主要介紹了mybatis-plus自動(dòng)填充插入更新時(shí)間有8小時(shí)時(shí)差,文中通過(guò)示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2021-09-09

最新評(píng)論

聂拉木县| 彭泽县| 淮阳县| 马关县| 荃湾区| 左云县| 阜宁县| 兴山县| 鹤庆县| 贡觉县| 登封市| 潞西市| 略阳县| 高密市| 房产| 东至县| 浮山县| 翼城县| 永安市| 罗江县| 嘉兴市| 交口县| 青川县| 辽源市| 垫江县| 陇西县| 木兰县| 永清县| 巴彦淖尔市| 太谷县| 岐山县| 东平县| 巴中市| 犍为县| 霍州市| 北流市| 射洪县| 大洼县| 通道| 武隆县| 辽中县|