☕ NEW! 完成新手任務即可參加抽獎!LINE 星巴克禮券等你拿,名額有限!        🎉 推廣活動:邀請好友註冊 DevLearn,累積推薦抽 LINE 星巴克禮券! 活動詳情 →        🔥 活動期間 2026/4/1 - 5/31 |已有 0 人參加       
redis 中級

⏰ 快取過期與失效:TTL、LRU、主動清除

📌 TTL(Time To Live)存活時間

每個 Redis 鍵都可以設定「到期時間」,時間到了自動刪除。

# 設定鍵並指定 TTL(秒)
SET product:1001 "{...}" EX 300     # 300 秒後過期

# 設定鍵並指定 TTL(毫秒)
SET product:1001 "{...}" PX 300000  # 300000 毫秒後過期

# 對已存在的鍵設定 TTL
EXPIRE product:1001 300
PEXPIRE product:1001 300000

# 查看剩餘 TTL
TTL product:1001      # 返回剩餘秒數,-1 表示永不過期,-2 表示不存在
PTTL product:1001     # 毫秒精度

# 移除 TTL(變成永不過期)
PERSIST product:1001

📌 .NET 中設定過期時間

使用 StackExchange.Redis

var db = redis.GetDatabase();

// 設定帶 TTL 的鍵
await db.StringSetAsync("session:abc", "user-data",
    TimeSpan.FromMinutes(30));

// 對已存在的鍵設定過期
await db.KeyExpireAsync("product:1001", TimeSpan.FromMinutes(5));

// 查看剩餘時間
TimeSpan? ttl = await db.KeyTimeToLiveAsync("product:1001");
Console.WriteLine($"剩餘: {ttl?.TotalSeconds} 秒");

// 移除過期(永不過期)
await db.KeyPersistAsync("product:1001");

使用 IDistributedCache

var options = new DistributedCacheEntryOptions();

// 絕對過期:從現在起 5 分鐘後過期(不管有沒有人存取)
options.AbsoluteExpirationRelativeToNow = TimeSpan.FromMinutes(5);

// 滑動過期:2 分鐘沒人存取就過期(每次存取會重設計時器)
options.SlidingExpiration = TimeSpan.FromMinutes(2);

// 兩者可以同時使用!
// 例:滑動 2 分鐘 + 絕對 5 分鐘
// → 持續被存取最多活 5 分鐘,2 分鐘沒人用就過期
options.AbsoluteExpirationRelativeToNow = TimeSpan.FromMinutes(5);
options.SlidingExpiration = TimeSpan.FromMinutes(2);

await _cache.SetStringAsync("product:1001", json, options);

📌 AbsoluteExpiration vs SlidingExpiration

絕對過期(Absolute):
├── 設定 ──── 5 分鐘後 ──── 過期(不管有沒有人用)

滑動過期(Sliding):
├── 設定 ── 存取 ── 存取 ── 2分鐘沒人用 ── 過期
│          ↑ 重設   ↑ 重設

同時使用:
├── 設定 ── 存取 ── 存取 ── 存取 ── 5分鐘到 ── 過期
│          ↑重設    ↑重設    ↑重設   (絕對上限)
類型 行為 適用場景
Absolute 固定時間後過期 商品資料、設定檔
Sliding 一段時間沒存取才過期 Session、使用者暫存
兩者合用 Sliding + 絕對上限 最佳實踐

📌 記憶體淘汰策略

當 Redis 記憶體用滿時,會根據設定的策略淘汰舊資料。

# 設定最大記憶體
maxmemory 256mb

# 設定淘汰策略
maxmemory-policy allkeys-lru

八種淘汰策略

策略 說明 適用場景
noeviction 不淘汰,滿了就報錯 不能丟資料
allkeys-lru 所有 key 中淘汰最近最少使用的 最常用
allkeys-lfu 所有 key 中淘汰最不常使用的 熱點資料場景
allkeys-random 隨機淘汰 隨意
volatile-lru 有設 TTL 的 key 中淘汰 LRU 混合持久/快取
volatile-lfu 有設 TTL 的 key 中淘汰 LFU 混合場景
volatile-random 有設 TTL 的 key 中隨機淘汰 混合場景
volatile-ttl 淘汰即將過期的 key TTL 管理嚴格

推薦: 純快取用 allkeys-lru,混合用途用 volatile-lru


📌 主動清除快取的時機與方法

public class CacheInvalidationService
{
    private readonly IDatabase _redis;

    // 1. 更新資料時清除
    public async Task OnProductUpdated(int productId)
    {
        await _redis.KeyDeleteAsync($"product:{productId}");
    }

    // 2. 批量清除某個前綴的所有快取
    public async Task ClearProductCache()
    {
        var server = _redis.Multiplexer.GetServer("localhost:6379");
        var keys = server.Keys(pattern: "product:*").ToArray();

        if (keys.Length > 0)
            await _redis.KeyDeleteAsync(keys);

        Console.WriteLine($"已清除 {keys.Length} 筆商品快取");
    }

    // 3. 使用 Pub/Sub 通知其他實例清除快取
    public async Task PublishCacheInvalidation(string key)
    {
        var sub = _redis.Multiplexer.GetSubscriber();
        await sub.PublishAsync("cache:invalidate", key);
    }
}

📌 範例:商品快取 + 30 秒 TTL

public class ProductCacheService
{
    private readonly IDistributedCache _cache;
    private readonly AppDbContext _db;

    private static readonly DistributedCacheEntryOptions _cacheOptions = new()
    {
        AbsoluteExpirationRelativeToNow = TimeSpan.FromSeconds(30),
        SlidingExpiration = TimeSpan.FromSeconds(10)
    };

    public async Task<Product?> GetProductAsync(int id)
    {
        var key = $"product:{id}";
        var cached = await _cache.GetStringAsync(key);

        if (cached != null)
            return JsonSerializer.Deserialize<Product>(cached);

        var product = await _db.Products.FindAsync(id);
        if (product != null)
        {
            await _cache.SetStringAsync(key,
                JsonSerializer.Serialize(product), _cacheOptions);
        }
        return product;
    }
}

🔑 重點整理

  1. TTL 是快取過期的基礎,用 EX/PX 或 EXPIRE 設定
  2. AbsoluteExpiration 固定時間過期,SlidingExpiration 閒置才過期
  3. 最佳實踐是 兩者合用,Sliding + 絕對上限
  4. 記憶體滿時,allkeys-lru 是最常用的淘汰策略
  5. 資料更新後要 主動清除快取,保持一致性

💡 大家的想法 · 0

載入中...
💬 即時聊天室 🟢 0 人在線
😀 😎 🤓 💻 🎮 🎸 🔥
➕ 新問題
📋 我的工單
💬 LINE 社群
🔒
需要註冊才能使用此功能
註冊帳號即可解鎖測驗、遊戲、簽到、筆記下載等所有功能,完全免費!
免費註冊