Redis-caching og meddelelser (Pub/Sub, Streams) · Lektion

Avancerede cachemønstre

Udforsk cachemønstrene Read-Through, Write-Back og Refresh-Ahead til komplekse scenarier.

Lektion 1 af 411 trin

Avancerede cachemønstre er en gratis Redis-caching og meddelelser (Pub/Sub, Streams)-lektion på CoddyKit. Dette er lektion 1 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 Redis-caching og meddelelser (Pub/Sub, Streams), og dine fremskridt synkroniseres på tværs af nettet og CoddyKit-appen. Redis-caching og meddelelser (Pub/Sub, Streams)-kurset indeholder 4 lektioner i alt.

Ud over grundlæggende caching

Vi har gennemgået grundlæggende cachemønstre som Cache-Aside. Men hvad med mere komplekse scenarier?

Avancerede mønstre hjælper os med at håndtere specifikke udfordringer som dataaktualitet, skriveydelse og opretholdelse af konsistens i distribuerede systemer.

Forståelse af Read-Through

Read-Through-mønsteret gør cachen ansvarlig for at hente data fra det underliggende datalager, hvis dataene ikke findes i cachen.

  • Applikationen beder cachen om data.
  • Hvis der opstår et cache-miss, henter cachen data fra databasen.
  • Cachen gemmer derefter disse data og returnerer dem til applikationen.
  • Applikationen arbejder altid med cachen, hvilket gør dens logik enklere.

Read-Through i praksis

Her er en forenklet idé om, hvordan en Read-Through-cache kan fungere. Bemærk, at applikationen ikke forespørger databasen direkte.

import java.util.HashMap;
import java.util.Map;

// Simplified Read-Through Cache concept
class ProductCache {
  private Map<String, String> cache = new HashMap<>();
  private DatabaseService db = new DatabaseService();

  public String getProduct(String productId) {
    // 1. Check cache
    if (cache.containsKey(productId)) {
      System.out.println("Cache hit for " + productId);
      return cache.get(productId);
    }

    // 2. Cache miss, fetch from DB
    System.out.println("Cache miss for " + productId + ", fetching from DB.");
    String productData = db.fetchProductFromDB(productId);

    // 3. Store in cache and return
    cache.put(productId, productData);
    return productData;
  }
}

class DatabaseService {
  public String fetchProductFromDB(String productId) {
    // Simulate DB call
    return "Product_" + productId + "_Details";
  }
}

public class Main {
  public static void main(String[] args) {
    ProductCache productCache = new ProductCache();
    System.out.println(productCache.getProduct("P1")); // Miss, then hit
    System.out.println(productCache.getProduct("P1")); // Hit
  }
}

Introduktion til Write-Back

Med Write-Back (eller Write-Behind) skrives data først til cachen, hvorefter cachen asynkront skriver dem til det underliggende datalager.

  • Applikationen skriver til cachen og får et hurtigt svar.
  • Cachen bekræfter skrivningen med det samme.
  • Cachen sætter skrivningen til databasen i kø til senere.
  • Det forbedrer skriveydelsen, men indebærer risiko for datatab, hvis cachen svigter, før synkroniseringen er gennemført.

Write-Back-logik

Dette eksempel viser, hvordan en skrivehandling først opdaterer cachen, mens opdateringen af databasen sker senere.

import java.util.HashMap;
import java.util.Map;

// Simplified Write-Back Cache concept
class DataCache {
  private Map<String, String> cache = new HashMap<>();
  private DatabaseService db = new DatabaseService();

  public void updateData(String key, String value) {
    // 1. Write to cache immediately
    cache.put(key, value);
    System.out.println("Data '" + key + "' updated in cache.");

    // 2. Schedule asynchronous write to DB
    // In a real system, this would be a separate thread/queue
    new Thread(() -> {
      try {
        Thread.sleep(100); // Simulate async DB write delay
        db.saveDataToDB(key, value);
        System.out.println("Data '" + key + "' written to DB asynchronously.");
      } catch (InterruptedException e) {
        Thread.currentThread().interrupt();
      }
    }).start();
  }
}

class DatabaseService {
  public void saveDataToDB(String key, String value) {
    // Simulate DB write
    System.out.println("Saving '" + key + ":" + value + "' to database.");
  }
}

public class Main {
  public static void main(String[] args) {
    DataCache dataCache = new DataCache();
    dataCache.updateData("User1", "NewEmail@example.com");
    System.out.println("Application continues immediately...");
    // In a real app, you'd handle cache shutdown gracefully to ensure writes complete.
  }
}

Bliv fortrolig med Refresh-Ahead

Refresh-Ahead-mønsteret opdaterer proaktivt cacheposter før de udløber med det formål at forhindre cache-misses.

  • Når et element tilgås, kontrolleres dets udløbstimer.
  • Hvis det nærmer sig udløb, henter cachen asynkront en opdateret kopi fra databasen.
  • Det sikrer, at næste tilgang rammer aktuelle data, hvilket reducerer brugernes svartid.
  • Det kræver omhyggelig justering af opdateringstærsklerne.

Refresh-Ahead i praksis

Dette kodestykke illustrerer, hvordan en Refresh-Ahead-strategi kan fungere, når et element tilgås, og dets aktualitet kontrolleres.

import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

// Simplified Refresh-Ahead Cache concept
class ItemCache {
  private ConcurrentHashMap<String, String> cache = new ConcurrentHashMap<>();
  private ConcurrentHashMap<String, Long> expirationTimes = new ConcurrentHashMap<>();
  private DatabaseService db = new DatabaseService();
  private ScheduledExecutorService scheduler = Executors.newSingleThreadScheduledExecutor();

  private final long CACHE_TTL_MS = 10000; // 10 seconds
  private final long REFRESH_THRESHOLD_MS = 2000; // Refresh 2 seconds before expiry

  public ItemCache() {
    // Simulate initial data load
    cache.put("ItemA", "DataA_V1");
    expirationTimes.put("ItemA", System.currentTimeMillis() + CACHE_TTL_MS);
  }

  public String getItem(String itemId) {
    if (cache.containsKey(itemId)) {
      long currentExpiry = expirationTimes.get(itemId);
      long timeToLive = currentExpiry - System.currentTimeMillis();

      // If item is nearing expiration, schedule a refresh
      if (timeToLive > 0 && timeToLive < REFRESH_THRESHOLD_MS) {
        System.out.println("Item " + itemId + " nearing expiry, scheduling refresh.");
        scheduler.schedule(() -> refreshItem(itemId), 0, TimeUnit.MILLISECONDS);
      }
      return cache.get(itemId);
    }
    // Fallback to read-through if not in cache (simplified)
    System.out.println("Item " + itemId + " not in cache, fetching fresh.");
    String data = db.fetchItemFromDB(itemId);
    cache.put(itemId, data);
    expirationTimes.put(itemId, System.currentTimeMillis() + CACHE_TTL_MS);
    return data;
  }

  private void refreshItem(String itemId) {
    System.out.println("Refreshing item " + itemId + " from DB...");
    String freshData = db.fetchItemFromDB(itemId + "_Refreshed"); // Simulate updated data
    cache.put(itemId, freshData);
    expirationTimes.put(itemId, System.currentTimeMillis() + CACHE_TTL_MS);
    System.out.println("Item " + itemId + " refreshed with " + freshData);
  }
}

class DatabaseService {
  public String fetchItemFromDB(String itemId) {
    // Simulate DB call
    return "Data for " + itemId + " from DB";
  }
}

public class Main {
  public static void main(String[] args) throws InterruptedException {
    ItemCache itemCache = new ItemCache();
    System.out.println("First access: " + itemCache.getItem("ItemA"));
    Thread.sleep(8500); // Wait until it's near expiration
    System.out.println("Second access (triggers refresh): " + itemCache.getItem("ItemA"));
    Thread.sleep(500); // Give refresh a chance to run
    System.out.println("Third access (should be refreshed): " + itemCache.getItem("ItemA"));
    // A real app would shut down the scheduler
  }
}

Sammenligning af mønstre

Hvert avanceret mønster har et særligt formål:

  • Read-Through: Gør applikationens logik enklere ved at lade cachen håndtere databasehentninger ved cache-misses.
  • Write-Back: Forbedrer skriveydelsen ved at udskyde skrivninger til databasen, men medfører risiko for datatab, hvis cachen svigter.
  • Refresh-Ahead: Forbedrer læsesvartiden ved proaktivt at opdatere populære cacheposter, før de udløber.

Vælg det rette mønster

Det bedste mønster afhænger af din applikations behov:

  • Brug Read-Through, når du vil adskille logikken til datahentning fra applikationen og forenkle samspillet med cachen.
  • Vælg Write-Back til skrivehandlinger med høj gennemstrømning, hvor et vist datatab kan accepteres, eller når der findes robuste mekanismer til datalagring.
  • Implementer Refresh-Ahead til læsetunge arbejdsbelastninger, hvor en konstant lav svartid er afgørende, især for data, der tilgås ofte.

Avanceret cachequiz

Forestil dig en applikation, hvor brugere ofte ser produktdetaljer, og opdateringer af produktlageret er kritiske, men kan udføres asynkront. Hvilke cachemønstre vil være bedst egnede til at sikre både hurtige læsninger og effektive skrivninger?

Opsummering: Avanceret caching

Vi har undersøgt tre avancerede cachemønstre: Read-Through, Write-Back og Refresh-Ahead. Hvert mønster har unikke fordele ved specifikke udfordringer med ydeevne og konsistens.

En forståelse af disse mønstre hjælper dig med at designe mere robuste og effektive cachestrategier til komplekse applikationer.

Gratis at komme i gang

Lær Redis-caching og meddelelser (Pub/Sub, Streams) 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
12
Lektioner
48

Ofte stillede spørgsmål

Er lektionen “Avancerede cachemønstre” gratis?

Ja — alle 3 lektioner i læringssporet Redis-caching og meddelelser (Pub/Sub, Streams), inklusive “Avancerede cachemønstre”, 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. Redis-caching og meddelelser (Pub/Sub, Streams)-kurset indeholder 4 lektioner i alt.

Hvad lærer jeg i “Avancerede cachemønstre”?

Udforsk cachemønstrene Read-Through, Write-Back og Refresh-Ahead til komplekse scenarier. Du øver dig i Redis-caching og meddelelser (Pub/Sub, Streams) 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å Redis-caching og meddelelser (Pub/Sub, Streams)?

Der kræves ingen tidligere erfaring. Redis-caching og meddelelser (Pub/Sub, Streams) 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 1 af 4.

Hvor lang tid tager lektionen “Avancerede cachemønstre”?

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 Redis-caching og meddelelser (Pub/Sub, Streams)-lektion?

Ja. Alle Redis-caching og meddelelser (Pub/Sub, Streams)-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. Avancerede cachemønstre
  2. Sessionshåndtering med Redis
  3. Rate limiting og anti-patterns
  4. Strategier til cache-invalidering
← Tilbage til Redis-caching og meddelelser (Pub/Sub, Streams)