Rumah  >  Artikel  >  pangkalan data  >  Cara Redis+Caffeine melaksanakan komponen cache peringkat kedua yang diedarkan

Cara Redis+Caffeine melaksanakan komponen cache peringkat kedua yang diedarkan

WBOY
WBOYke hadapan
2023-05-30 23:10:58895semak imbas

Cache tahap kedua yang dipanggil

Cache ialah untuk membaca data daripada medium bacaan yang lebih perlahan dan meletakkannya pada medium bacaan yang lebih pantas, seperti cakera-->memori.

Biasanya kami menyimpan data pada cakera, seperti pangkalan data. Jika anda membaca dari pangkalan data setiap kali, kelajuan membaca akan dipengaruhi oleh IO cakera itu sendiri, jadi terdapat cache memori seperti redis. Data boleh dibaca dan dimasukkan ke dalam memori, supaya apabila data perlu diperolehi, data boleh dikembalikan terus dari memori, yang boleh meningkatkan kelajuan.
Tetapi secara amnya redis digunakan secara berasingan ke dalam gugusan, jadi akan terdapat penggunaan rangkaian IO Walaupun pautan dengan gugusan redis sudah mempunyai alat seperti kumpulan sambungan, masih terdapat penggunaan penghantaran data tertentu. Jadi terdapat cache dalam proses, seperti kafein. Apabila cache dalam aplikasi mempunyai data yang layak, ia boleh digunakan secara langsung tanpa perlu mendapatkannya daripada redis melalui rangkaian. Ini membentuk cache dua peringkat. Cache dalam aplikasi dipanggil cache peringkat pertama, dan cache jauh (seperti redis) dipanggil cache peringkat kedua.

  • Adakah sistem perlu cache penggunaan CPU: Jika anda mempunyai beberapa aplikasi yang perlu menggunakan banyak CPU untuk mengira dan mendapatkan hasil.

  • Jika kumpulan sambungan pangkalan data anda agak melahu, anda tidak seharusnya menggunakan cache untuk menduduki sumber IO pangkalan data. Pertimbangkan untuk menggunakan caching apabila kumpulan sambungan pangkalan data sibuk atau kerap melaporkan amaran tentang sambungan yang tidak mencukupi.

Kelebihan cache peringkat kedua yang diedarkan

Redis digunakan untuk menyimpan data panas dan data yang tiada dalam Redis diakses terus daripada pangkalan data.
Kami sudah mempunyai Redis, mengapa kami perlu tahu tentang cache proses seperti Jambu Batu dan Kafein:

  • Jika Redis tidak tersedia, kami hanya boleh mengakses pangkalan data pada masa ini , yang boleh menyebabkan runtuhan salji dengan mudah, tetapi ini biasanya tidak berlaku.

  • Mengakses Redis akan mempunyai I/O rangkaian tertentu dan overhed pensirilan Walaupun prestasinya sangat tinggi, ia tidak sepantas kaedah tempatan disimpan dalam tempatan untuk mempercepatkan lagi akses. Idea ini bukan unik untuk seni bina Internet kami. Kami menggunakan cache berbilang peringkat L1, L2, dan L3 dalam sistem komputer untuk mengurangkan akses terus ke memori dan dengan itu mempercepatkan akses.

Jadi jika kita hanya menggunakan Redis, ia boleh memenuhi kebanyakan keperluan kita, tetapi apabila kita perlu mengejar prestasi yang lebih tinggi dan ketersediaan yang lebih tinggi, kita perlu memahami cache berbilang peringkat .

Perihalan proses membaca data proses operasi cache Tahap 2

Cara Redis+Caffeine melaksanakan komponen cache peringkat kedua yang diedarkan

Apabila redis mahupun cache setempat tidak boleh menanyakan nilai, proses kemas kini akan dicetuskan keseluruhan proses adalah Perihalan proses pembatalan cache Terkunci

Cara Redis+Caffeine melaksanakan komponen cache peringkat kedua yang diedarkan

kemas kini redis dan pemadaman kunci cache akan dicetuskan Selepas mengosongkan cache redis

cara menggunakan komponen ?

Komponen diubah suai berdasarkan rangka kerja Cache Spring Untuk menggunakan cache yang diedarkan dalam projek, anda hanya perlu menambah: cacheManager ="L2_CacheManager", atau cacheManager = CacheRedisCaffeineAutoConfiguration

//这个方法会使用分布式二级缓存来提供查询
@Cacheable(cacheNames = CacheNames.CACHE_12HOUR, cacheManager = "L2_CacheManager")
public Config getAllValidateConfig() { 
}

Jika anda ingin menggunakan kedua-dua cache yang diedarkan dan komponen cache peringkat kedua yang diedarkan, maka anda perlu menyuntik kacang @Primary CacheManager ke dalam Spring

@Primary
@Bean("deaultCacheManager")
public RedisCacheManager cacheManager(RedisConnectionFactory factory) {
    // 生成一个默认配置,通过config对象即可对缓存进行自定义配置
    RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig();
    // 设置缓存的默认过期时间,也是使用Duration设置
    config = config.entryTtl(Duration.ofMinutes(2)).disableCachingNullValues();

    // 设置一个初始化的缓存空间set集合
    Set<String> cacheNames =  new HashSet<>();
    cacheNames.add(CacheNames.CACHE_15MINS);
    cacheNames.add(CacheNames.CACHE_30MINS);

    // 对每个缓存空间应用不同的配置
    Map<String, RedisCacheConfiguration> configMap = new HashMap<>();
    configMap.put(CacheNames.CACHE_15MINS, config.entryTtl(Duration.ofMinutes(15)));
    configMap.put(CacheNames.CACHE_30MINS, config.entryTtl(Duration.ofMinutes(30)));
  
    // 使用自定义的缓存配置初始化一个cacheManager
    RedisCacheManager cacheManager = RedisCacheManager.builder(factory)
        .initialCacheNames(cacheNames)  // 注意这两句的调用顺序,一定要先调用该方法设置初始化的缓存名,再初始化相关的配置
        .withInitialCacheConfigurations(configMap)
        .build();
    return cacheManager;
}

Kemudian:

//这个方法会使用分布式二级缓存
@Cacheable(cacheNames = CacheNames.CACHE_12HOUR, cacheManager = "L2_CacheManager")
public Config getAllValidateConfig() {
}

//这个方法会使用分布式缓存
@Cacheable(cacheNames = CacheNames.CACHE_12HOUR)
public Config getAllValidateConfig2() {
}

Pelaksanaan teras kaedah

Inti sebenarnya adalah untuk melaksanakan antara muka org.springframework.cache.CacheManager dan mewarisi org.springframework.cache.support.AbstractValueAdaptingCache untuk melaksanakan pembacaan dan penulisan cache di bawah rangka kerja cache Spring.

RedisCaffeineCacheManager melaksanakan antara muka CacheManager

RedisCaffeineCacheManager.class terutamanya menguruskan kejadian cache, menjana kacang pengurusan cache yang sepadan berdasarkan CacheNames yang berbeza, dan kemudian meletakkannya ke dalam peta.

package com.axin.idea.rediscaffeinecachestarter.support;

import com.axin.idea.rediscaffeinecachestarter.CacheRedisCaffeineProperties;
import com.github.benmanes.caffeine.cache.Caffeine;
import com.github.benmanes.caffeine.cache.stats.CacheStats;
import lombok.extern.slf4j.Slf4j;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.cache.Cache;
import org.springframework.cache.CacheManager;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.util.CollectionUtils;

import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.TimeUnit;

@Slf4j
public class RedisCaffeineCacheManager implements CacheManager {

    private final Logger logger = LoggerFactory.getLogger(RedisCaffeineCacheManager.class);

    private static ConcurrentMap<String, Cache> cacheMap = new ConcurrentHashMap<String, Cache>();

    private CacheRedisCaffeineProperties cacheRedisCaffeineProperties;

    private RedisTemplate<Object, Object> stringKeyRedisTemplate;

    private boolean dynamic = true;

    private Set<String> cacheNames;
    {
        cacheNames = new HashSet<>();
        cacheNames.add(CacheNames.CACHE_15MINS);
        cacheNames.add(CacheNames.CACHE_30MINS);
        cacheNames.add(CacheNames.CACHE_60MINS);
        cacheNames.add(CacheNames.CACHE_180MINS);
        cacheNames.add(CacheNames.CACHE_12HOUR);
    }
    public RedisCaffeineCacheManager(CacheRedisCaffeineProperties cacheRedisCaffeineProperties,
                                     RedisTemplate<Object, Object> stringKeyRedisTemplate) {
        super();
        this.cacheRedisCaffeineProperties = cacheRedisCaffeineProperties;
        this.stringKeyRedisTemplate = stringKeyRedisTemplate;
        this.dynamic = cacheRedisCaffeineProperties.isDynamic();
    }

    //——————————————————————— 进行缓存工具 ——————————————————————
    /**
    * 清除所有进程缓存
    */
    public void clearAllCache() {
        stringKeyRedisTemplate.convertAndSend(cacheRedisCaffeineProperties.getRedis().getTopic(), new CacheMessage(null, null));
    }

    /**
    * 返回所有进程缓存(二级缓存)的统计信息
    * result:{"缓存名称":统计信息}
    * @return
    */
    public static Map<String, CacheStats> getCacheStats() {
        if (CollectionUtils.isEmpty(cacheMap)) {
            return null;
        }

        Map<String, CacheStats> result = new LinkedHashMap<>();
        for (Cache cache : cacheMap.values()) {
            RedisCaffeineCache caffeineCache = (RedisCaffeineCache) cache;
            result.put(caffeineCache.getName(), caffeineCache.getCaffeineCache().stats());
        }
        return result;
    }

    //—————————————————————————— core —————————————————————————
    @Override
    public Cache getCache(String name) {
        Cache cache = cacheMap.get(name);
        if(cache != null) {
            return cache;
        }
        if(!dynamic && !cacheNames.contains(name)) {
            return null;
        }

        cache = new RedisCaffeineCache(name, stringKeyRedisTemplate, caffeineCache(name), cacheRedisCaffeineProperties);
        Cache oldCache = cacheMap.putIfAbsent(name, cache);
        logger.debug("create cache instance, the cache name is : {}", name);
        return oldCache == null ? cache : oldCache;
    }

    @Override
    public Collection<String> getCacheNames() {
        return this.cacheNames;
    }

    public void clearLocal(String cacheName, Object key) {
        //cacheName为null 清除所有进程缓存
        if (cacheName == null) {
            log.info("清除所有本地缓存");
            cacheMap = new ConcurrentHashMap<>();
            return;
        }

        Cache cache = cacheMap.get(cacheName);
        if(cache == null) {
            return;
        }

        RedisCaffeineCache redisCaffeineCache = (RedisCaffeineCache) cache;
        redisCaffeineCache.clearLocal(key);
    }

    /**
    * 实例化本地一级缓存
    * @param name
    * @return
    */
    private com.github.benmanes.caffeine.cache.Cache<Object, Object> caffeineCache(String name) {
        Caffeine<Object, Object> cacheBuilder = Caffeine.newBuilder();
        CacheRedisCaffeineProperties.CacheDefault cacheConfig;
        switch (name) {
            case CacheNames.CACHE_15MINS:
                cacheConfig = cacheRedisCaffeineProperties.getCache15m();
                break;
            case CacheNames.CACHE_30MINS:
                cacheConfig = cacheRedisCaffeineProperties.getCache30m();
                break;
            case CacheNames.CACHE_60MINS:
                cacheConfig = cacheRedisCaffeineProperties.getCache60m();
                break;
            case CacheNames.CACHE_180MINS:
                cacheConfig = cacheRedisCaffeineProperties.getCache180m();
                break;
            case CacheNames.CACHE_12HOUR:
                cacheConfig = cacheRedisCaffeineProperties.getCache12h();
                break;
            default:
                cacheConfig = cacheRedisCaffeineProperties.getCacheDefault();
        }
        long expireAfterAccess = cacheConfig.getExpireAfterAccess();
        long expireAfterWrite = cacheConfig.getExpireAfterWrite();
        int initialCapacity = cacheConfig.getInitialCapacity();
        long maximumSize = cacheConfig.getMaximumSize();
        long refreshAfterWrite = cacheConfig.getRefreshAfterWrite();

        log.debug("本地缓存初始化:");
        if (expireAfterAccess > 0) {
            log.debug("设置本地缓存访问后过期时间,{}秒", expireAfterAccess);
            cacheBuilder.expireAfterAccess(expireAfterAccess, TimeUnit.SECONDS);
        }
        if (expireAfterWrite > 0) {
            log.debug("设置本地缓存写入后过期时间,{}秒", expireAfterWrite);
            cacheBuilder.expireAfterWrite(expireAfterWrite, TimeUnit.SECONDS);
        }
        if (initialCapacity > 0) {
            log.debug("设置缓存初始化大小{}", initialCapacity);
            cacheBuilder.initialCapacity(initialCapacity);
        }
        if (maximumSize > 0) {
            log.debug("设置本地缓存最大值{}", maximumSize);
            cacheBuilder.maximumSize(maximumSize);
        }
        if (refreshAfterWrite > 0) {
            cacheBuilder.refreshAfterWrite(refreshAfterWrite, TimeUnit.SECONDS);
        }
        cacheBuilder.recordStats();
        return cacheBuilder.build();
    }
}

RedisCaffeineCache mewarisi AbstractValueAdaptingCache

Intinya ialah kaedah dapatkan dan kaedah put.

rreeee

Atas ialah kandungan terperinci Cara Redis+Caffeine melaksanakan komponen cache peringkat kedua yang diedarkan. Untuk maklumat lanjut, sila ikut artikel berkaitan lain di laman web China PHP!

Kenyataan:
Artikel ini dikembalikan pada:yisu.com. Jika ada pelanggaran, sila hubungi admin@php.cn Padam