Komplet guide til Spring Boot 4 · Lektion

Distribueret caching med Redis og TTL'er

Del cachestate på tværs af instanser ved at bruge Redis som central cache-backend med kontrol over serialisering.

Lektion 3 af 413 trin

Distribueret caching med Redis og TTL'er er en gratis Komplet guide til Spring Boot 4-lektion på CoddyKit. Dette er lektion 3 af 4. Du kan læse alle 3 lektioner i dette læringsspor gratis i deres fulde længde — derefter låser CoddyKit PRO alle lektioner op samt praktiske øvelser med en indbygget kodeeditor og en AI-underviser døgnet rundt. Den er en del af læringsforløbet i Komplet guide til Spring Boot 4, og dine fremskridt synkroniseres på tværs af nettet og CoddyKit-appen. Komplet guide til Spring Boot 4-kurset indeholder 4 lektioner i alt.

Hvorfor distribuere caching?

Caffeine er en fremragende cache i processen: ekstremt hurtig, men hver instans af programmet har sin egen private kopi. Så snart du skalerer vandret til flere pods, begynder kopierne at afvige fra hinanden.

  • Instans A fjerner en bruger; instans B leverer stadig den forældede indgang.
  • Opvarmning af cachen på én node hjælper ingen andre.
  • De samlede hukommelsesomkostninger vokser lineært med antallet af replikaer.

En distribueret cache løser dette ved at placere cachetilstanden i et delt bagvedliggende system, som alle instanser læser fra og skriver til. I Spring Boot er Redis det mest almindelige valg til denne rolle.

Tilføjelse af Redis-cache-starteren

Spring Boots caching-abstraktion er uafhængig af det bagvedliggende system. Hvis du vil skifte Caffeine ud med Redis, ændrer du hovedsageligt afhængigheder og en egenskab — dine @Cacheable-annoteringer forbliver de samme.

Tilføj Redis-starteren og caching-abstraktionen:

  • spring-boot-starter-data-redis leverer forbindelsen og RedisCacheManager.
  • spring-boot-starter-cache aktiverer @Cacheable/@CacheEvict-annoteringerne.

Angiv derefter Redis som cachetypen, så Boot automatisk konfigurerer en RedisCacheManager i stedet for et simpelt map.

<!-- pom.xml -->
<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-data-redis</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-cache</artifactId>
    </dependency>
</dependencies>

Konfiguration af forbindelse og cachetype

Angiv din Redis-server for Boot, og fortæl caching-abstraktionen, at den skal bruge Redis. Med spring.cache.type=redis tilslutter Boot automatisk en RedisCacheManager.

  • spring.data.redis.host / port konfigurerer Lettuce-klienten (standarddriveren).
  • spring.cache.redis.time-to-live angiver en standard-TTL for alle indgange.
  • spring.cache.cache-names kan erklære caches på forhånd ved opstart.
# application.yml
spring:
  data:
    redis:
      host: localhost
      port: 6379
  cache:
    type: redis
    cache-names: products, users
    redis:
      time-to-live: 10m
      cache-null-values: false
      use-key-prefix: true

Den samme @Cacheable, et delt bagvedliggende system

Det er fordelen ved Springs abstraktion: Tjenestekoden er identisk med Caffeine-versionen. Det er kun CacheManager bagved, der er ændret.

Når instans A nu udfylder products::42, læser instans B præcis den samme nøgle fra Redis ved sit næste kald — ingen dobbelt beregning og ingen afvigelser.

@Service
public class ProductService {

    private final ProductRepository repository;

    public ProductService(ProductRepository repository) {
        this.repository = repository;
    }

    @Cacheable(cacheNames = "products", key = "#id")
    public Product findById(Long id) {
        // Runs only on a cache miss across the whole cluster
        return repository.findById(id)
                .orElseThrow(() -> new ProductNotFoundException(id));
    }

    @CacheEvict(cacheNames = "products", key = "#product.id")
    public Product update(Product product) {
        return repository.save(product);
    }
}

TTL'er: Begrænsning af forældelse

En TTL (levetid) er den maksimale alder for en cacheindgang, før Redis automatisk fjerner den. TTL'er er det vigtigste værn mod at levere forældede data fra en distribueret cache.

  • Kort TTL (sekunder) → friskere data, større belastning på det bagvedliggende system.
  • Lang TTL (timer) → billigere, men risikoen for forældede data vokser.
  • TTL håndhæves på serversiden af Redis, så den gælder ens for alle instanser.

I modsætning til Caffeine's expireAfterWrite overlever Redis' TTL'er en genstart af en enkelt instans, fordi dataene ligger uden for JVM'en.

TTL pr. cache med en brugerdefineret RedisCacheManager

Én global TTL passer sjældent til alle caches. Tilsidesæt den automatiske konfiguration for at give hver cache sit eget udløb ved at levere en RedisCacheManagerBuilderCustomizer (eller en komplet RedisCacheManager-bean).

Her accepterer products 30 minutters forældelse, mens de omskiftelige prices udløber efter 1 minut.

@Configuration
public class CacheConfig {

    @Bean
    public RedisCacheManagerBuilderCustomizer cacheCustomizer() {
        return builder -> builder
            .withCacheConfiguration("products",
                RedisCacheConfiguration.defaultCacheConfig()
                    .entryTtl(Duration.ofMinutes(30)))
            .withCacheConfiguration("prices",
                RedisCacheConfiguration.defaultCacheConfig()
                    .entryTtl(Duration.ofMinutes(1)));
    }
}

Serialisering: Sådan når værdierne Redis

Redis gemmer bytes, ikke Java-objekter. Alle cachede værdier skal serialiseres ved skrivning og deserialiseres ved læsning. Standardindstillingen for RedisCacheManager bruger Javas native serialisering (JdkSerializationRedisSerializer), som har reelle ulemper:

  • Værdierne er uigennemsigtige binære klumper — de kan ikke læses med redis-cli.
  • Den cachede klasse skal implementere Serializable.
  • Tæt kobling til klassens interne detaljer går i stykker på tværs af versioner.

Hvis du vil have interoperable og læsbare indgange, skal du skifte værdiernes serialisering til JSON.

Styring af serialisering med JSON

Brug GenericJackson2JsonRedisSerializer til værdier og en almindelig StringRedisSerializer til nøgler. JSON gør indgangene nemme at inspicere og kobler dem fra Javas interne klassedetaljer.

Serialiseringsværktøjet indlejrer typemetadata (@class), så polymorfe værdier deserialiseres til den korrekte konkrete type.

@Bean
public RedisCacheConfiguration cacheConfiguration() {
    return RedisCacheConfiguration.defaultCacheConfig()
        .entryTtl(Duration.ofMinutes(10))
        .disableCachingNullValues()
        .serializeKeysWith(
            RedisSerializationContext.SerializationPair.fromSerializer(
                new StringRedisSerializer()))
        .serializeValuesWith(
            RedisSerializationContext.SerializationPair.fromSerializer(
                new GenericJackson2JsonRedisSerializer()));
}

Nøglepræfikser forhindrer sammenstød

Når flere caches deler én Redis-database, må deres nøgler ikke kollidere. Som standard sætter Spring cache-navnet foran hver indgang, så products::42 og users::42 forbliver adskilt.

  • use-key-prefix: true (standard) holder caches isolerede.
  • Du kan angive et brugerdefineret præfiks, f.eks. et app- eller tenantnavn, så en Redis-instans kan deles sikkert mellem tjenester.

Det er især vigtigt i opsætninger med flere tenants eller delt infrastruktur, hvor én Redis-instans betjener mange apps.

@Bean
public RedisCacheConfiguration cacheConfiguration() {
    return RedisCacheConfiguration.defaultCacheConfig()
        .entryTtl(Duration.ofMinutes(10))
        .computePrefixWith(cacheName -> "shop:" + cacheName + "::");
    // key becomes shop:products::42
}

Undgå stormløbet

En distribueret cache koncentrerer risikoen: Når en varm nøgles TTL udløber, rammer alle instanser et miss på én gang og overbelaster databasen — problemet med stormløbet.

Mulige løsninger:

  • Forskyd TTL'er ved at tilføje en lille tilfældig variation, så nøglerne ikke udløber samtidig.
  • Genindlæs indgange proaktivt før udløb i stedet for først ved et miss.
  • Brug en kortvarig lås, så kun én instans beregner en manglende nøgle igen, mens de øvrige venter.

Dette hjælpeværktøj i ren Java viser, hvordan du beregner en TTL med tilfældig variation, som du ville give videre til entryTtl(...).

import java.time.Duration;
import java.util.concurrent.ThreadLocalRandom;

public class TtlJitter {

    static Duration withJitter(Duration base, double jitterFraction) {
        long baseMs = base.toMillis();
        long spread = (long) (baseMs * jitterFraction);
        long offset = ThreadLocalRandom.current().nextLong(-spread, spread + 1);
        return Duration.ofMillis(baseMs + offset);
    }

    public static void main(String[] args) {
        Duration base = Duration.ofMinutes(10);
        for (int i = 0; i < 3; i++) {
            Duration ttl = withJitter(base, 0.1); // +/- 10%
            System.out.println("TTL seconds: " + ttl.getSeconds());
        }
    }
}

Caching i to niveauer: Caffeine + Redis

Du behøver ikke vælge. Et almindeligt mønster i produktion er en cache i to niveauer (nær-cache):

  • L1 — Caffeine i hver instans, meget kort TTL, håndterer hyppige læsninger med nanosekunders hastighed.
  • L2 — Redis, delt på tværs af klyngen, den autoritative kilde til cachetilstanden.

En forespørgsel tjekker først Caffeine; ved et miss falder den tilbage til Redis; ved et miss i Redis rammer den databasen. Det reducerer netværkskald og holder samtidig klyngen konsistent. Spring kan sammensætte dette med en CompositeCacheManager eller et dedikeret bibliotek til nær-caching.

Hurtigt tjek: Valg af TTL-strategi

Test din forståelse af afvejningerne ved distribueret caching.

Opsummering: Distribueret caching med Redis

Du er gået fra en privat cache i processen til en delt, distribueret cache:

  • Hvorfor Redis: Én cachetilstand på tværs af alle instanser eliminerer afvigelser og dobbelt arbejde.
  • Direkte udskiftning: Angiv spring.cache.type=redis, så forbliver din @Cacheable-kode uændret.
  • TTL'er: Redis håndhæver udløb på serversiden ensartet på tværs af klyngen og bevarer det ved genstart af instanser. Justér pr. cache med en RedisCacheManagerBuilderCustomizer.
  • Serialisering: Foretræk GenericJackson2JsonRedisSerializer til læsbare værdier, der tåler versionsændringer, frem for JDK's standardserialisering.
  • Nøglepræfikser isolerer caches, der deler én Redis-instans; tilfældig TTL-variation og caches i to niveauer med Caffeine+Redis dæmper stormløbet.
Gratis at komme i gang

Lær Java med en AI-underviser — gratis

Skriv og kør rigtig kode i din browser, få øjeblikkelig hjælp fra en AI-underviser døgnet rundt, og fortsæt, hvor du slap, på web eller i appen.

Kurser
21
Lektioner
84

Ofte stillede spørgsmål

Er lektionen “Distribueret caching med Redis og TTL'er” gratis?

Ja — alle 3 lektioner i læringssporet Komplet guide til Spring Boot 4, inklusive “Distribueret caching med Redis og TTL'er”, kan læses gratis i deres fulde længde her på webstedet. Derefter låser CoddyKit PRO alle lektioner op samt interaktive øvelser med en indbygget kodeeditor og en AI-underviser døgnet rundt. Komplet guide til Spring Boot 4-kurset indeholder 4 lektioner i alt.

Hvad lærer jeg i “Distribueret caching med Redis og TTL'er”?

Del cachestate på tværs af instanser ved at bruge Redis som central cache-backend med kontrol over serialisering. Du øver dig i Komplet guide til Spring Boot 4 med praktisk kode, som du kører direkte i browseren, og en AI-vejleder døgnet rundt besvarer dine spørgsmål, mens du arbejder dig gennem lektionen.

Skal jeg have erfaring for at begynde på Komplet guide til Spring Boot 4?

Der kræves ingen tidligere erfaring. Komplet guide til Spring Boot 4 på CoddyKit er tilrettelagt for både begyndere og øvede, så du kan starte her eller fra begyndelsen og lære i dit eget tempo. Dette er lektion 3 af 4.

Hvor lang tid tager lektionen “Distribueret caching med Redis og TTL'er”?

De fleste CoddyKit-lektioner tager cirka 5–10 minutter. Hver lektion er kort og interaktiv, så du gør løbende fremskridt og kan fortsætte, hvor du slap – på både web og app.

Kan jeg skrive og køre kode i denne Komplet guide til Spring Boot 4-lektion?

Ja. Alle Komplet guide til Spring Boot 4-lektioner har en indbygget kodeeditor, så du kan skrive og køre rigtig kode direkte i din browser og få øjeblikkelig feedback fra AI – uden lokal opsætning.

Alle lektioner i dette kursus

  1. Grundlæggende Spring Cache-abstraktion
  2. In-memory-caching med tuning af Caffeine
  3. Distribueret caching med Redis og TTL'er
  4. Cache stampede, invalidation og konsistens
← Tilbage til Komplet guide til Spring Boot 4