0Pricing
Redis Caching & Messaging (Pub/Sub, Streams) · บทเรียน

รูปแบบแคชขั้นสูง

สำรวจรูปแบบการแคชแบบอ่านผ่าน เขียนย้อนกลับ และรีเฟรชล่วงหน้า สำหรับสถานการณ์ที่ซับซ้อน

รูปแบบแคชขั้นสูง เป็นบทเรียน Redis Caching & Messaging (Pub/Sub, Streams) ฟรีบน CoddyKit นี่คือบทเรียนที่ 1 จากทั้งหมด 4 บทเรียน คุณสามารถอ่านบทเรียนทั้งหมดด้านล่างฟรี — จากนั้นลองปฏิบัติด้วยตัวคุณเองในเบราว์เซอร์พร้อมตัวแก้ไขโค้ดในตัวและติวเตอร์ AI ตลอด 24/7 บทเรียนนี้เป็นส่วนหนึ่งของเส้นทางการเรียน Redis Caching & Messaging (Pub/Sub, Streams) และความก้าวหน้าของคุณจะซิงค์ข้ามเว็บและแอป CoddyKit คอร์ส Redis Caching & Messaging (Pub/Sub, Streams) มีบทเรียนทั้งหมด 4 บทเรียน

บางส่วนของบทเรียนนี้ยังไม่ได้รับการแปล และแสดงเป็นภาษาอังกฤษ

Beyond Basic Caching

We've covered basic caching patterns like Cache-Aside. But what about more complex scenarios?

Advanced patterns help us handle specific challenges like data freshness, write performance, and maintaining consistency in distributed systems.

Understanding Read-Through

The Read-Through pattern makes the cache responsible for fetching data from the underlying data store if it's not present.

  • The application asks the cache for data.
  • If a cache miss occurs, the cache fetches data from the database.
  • The cache then stores this data and returns it to the application.
  • The application always interacts with the cache, simplifying its logic.

Read-Through in Action

Here's a simplified idea of how a read-through cache might work. Notice the application doesn't directly query the database.

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
  }
}

Introducing Write-Back

With Write-Back (or Write-Behind), data is written initially to the cache, and the cache then asynchronously writes it to the underlying data store.

  • Application writes to the cache, gets a quick response.
  • Cache acknowledges the write immediately.
  • Cache queues the write to the database for later.
  • This improves write performance but risks data loss if the cache fails before syncing.

Write-Back Logic

This example shows how a write operation would first update the cache, with the database update happening later.

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.
  }
}

Mastering Refresh-Ahead

The Refresh-Ahead pattern proactively updates cache entries before they expire, aiming to prevent cache misses.

  • When an item is accessed, its expiration timer is checked.
  • If it's nearing expiration, the cache asynchronously fetches a fresh copy from the database.
  • This ensures the next access hits fresh data, reducing latency for users.
  • It requires careful tuning of refresh thresholds.

Refresh-Ahead in Practice

This snippet illustrates how a refresh-ahead strategy might work when an item is accessed, checking its freshness.

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
  }
}

Pattern Comparison

Each advanced pattern serves a unique purpose:

  • Read-Through: Simplifies application logic by having the cache handle database fetches on misses.
  • Write-Back: Boosts write performance by deferring database writes, but introduces risk of data loss on cache failure.
  • Refresh-Ahead: Improves read latency by proactively updating popular cache entries before they expire.

Choosing the Right Pattern

The best pattern depends on your application's needs:

  • Use Read-Through when you want to abstract data fetching logic from the application and simplify cache interaction.
  • Choose Write-Back for high-throughput write operations where some data loss can be tolerated, or when robust persistence mechanisms are in place.
  • Implement Refresh-Ahead for read-heavy workloads where consistent low latency is critical, especially for frequently accessed data.

Advanced Cache Quiz

Consider an application where users frequently view product details, and updates to product stock are critical but can happen asynchronously. Which caching patterns would be most suitable to ensure both fast reads and efficient writes?

Recap: Advanced Caching

We explored three advanced caching patterns: Read-Through, Write-Back, and Refresh-Ahead. Each offers unique advantages for specific performance and consistency challenges.

Understanding these patterns helps you design more robust and performant caching strategies for complex applications.

คำถามที่พบบ่อย

บทเรียน “รูปแบบแคชขั้นสูง” ฟรีหรือไม่

ใช่ — ข้อความเต็มของ “รูปแบบแคชขั้นสูง” ฟรีให้อ่านที่นี่บนเว็บ เพื่อปฏิบัติแบบโต้ตอบ (ตัวแก้ไขโค้ดในตัวและติวเตอร์ AI ตลอด 24/7) และปลดล็อคส่วนที่เหลือของคอร์ส Redis Caching & Messaging (Pub/Sub, Streams) ให้อัปเกรดเป็น CoddyKit PRO คอร์ส Redis Caching & Messaging (Pub/Sub, Streams) มีบทเรียนทั้งหมด 4 บทเรียน

คุณจะเรียนรู้อะไรในบทเรียน “รูปแบบแคชขั้นสูง”

สำรวจรูปแบบการแคชแบบอ่านผ่าน เขียนย้อนกลับ และรีเฟรชล่วงหน้า สำหรับสถานการณ์ที่ซับซ้อน คุณปฏิบัติ Redis Caching & Messaging (Pub/Sub, Streams) ด้วยโค้ดที่ใช้งานได้จริงที่คุณเรียกใช้โดยตรงในเบราว์เซอร์ และติวเตอร์ AI ตลอด 24/7 ตอบคำถามของคุณขณะที่คุณไปผ่านบทเรียน

คุณต้องมีประสบการณ์ก่อนที่จะเริ่มเรียน Redis Caching & Messaging (Pub/Sub, Streams) หรือไม่

ไม่จำเป็นต้องมีประสบการณ์มาก่อน Redis Caching & Messaging (Pub/Sub, Streams) บน CoddyKit ออกแบบมาสำหรับผู้เริ่มต้นไปจนถึงผู้เรียนขั้นสูง คุณสามารถเริ่มต้นที่นี่หรือเริ่มจากตัวแรกและเรียนด้วยความเร็วของคุณเอง นี่คือบทเรียนที่ 1 จากทั้งหมด 4 บทเรียน

บทเรียน “รูปแบบแคชขั้นสูง” ใช้เวลานานแค่ไหน

บทเรียน CoddyKit ส่วนใหญ่ใช้เวลาประมาณ 5–10 นาที แต่ละบทเรียนจึงสั้นและเป็นแบบโต้ตอบ คุณสามารถก้าวหน้าอย่างต่อเนื่องและกลับมาเรียนต่อจากตรงที่เพิ่งหยุดบนเว็บและแอปได้เลย

ฉันเขียนและรันโค้ดในบทเรียน Redis Caching & Messaging (Pub/Sub, Streams) นี้ได้ไหม

ได้ บทเรียน Redis Caching & Messaging (Pub/Sub, Streams) ทุกบทมีตัวแก้ไขโค้ดในตัว คุณจึงเขียนและรันโค้ดจริงได้เลยในเบราว์เซอร์ และได้รับข้อเสนอแนะจาก AI ในทันที — ไม่ต้องติดตั้งในเครื่องของคุณ

บทเรียนทั้งหมดในหลักสูตรนี้

  1. รูปแบบแคชขั้นสูง
  2. การจัดการเซสชันด้วย Redis
  3. การจำกัดอัตราและรูปแบบการใช้งานที่ควรหลีกเลี่ยง
  4. กลยุทธ์การทำให้แคชใช้ไม่ได้
← กลับไปที่ Redis Caching & Messaging (Pub/Sub, Streams)