首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> .NET 網頁程式設計 >> ASP.NET 基礎 >> ASP.NET 基礎

ASP.NET (C#) 遍歷 Memcached 快取物件完整實作與 DiscuzNT Bug 修正

     編輯:ASP.NET 基礎

背景與挑戰

Memcached 為高效能分散式快取系統,但出於效能考量,不提供原生遍歷/列舉所有 Key 的指令。若需實作「查看所有快取內容」或「依條件清除」等管理功能,必須透過 stats 指令間接達成。本文完整記錄從 stats items 取得 slab ID,再用 stats cachedump 列出 Key,並修正 DiscuzNT 3.0 客戶端無法解析 ITEM 開頭回應的 Bug。

核心 Stats 指令說明

指令 用途 回應格式範例
stats items顯示各 slab 中 item 數量STAT items:1:number 50
stats cachedump <slab_id> <limit>列出指定 slab 前 N 個 KeyITEM mykey [123 b; 1234567890 s]
stats一般統計資訊STAT pid 1234
stats slabsSlab 分配細節...

DiscuzNT 客戶端核心程式碼分析

專案引用 Discuz.Cache 專案中的 MemCached.cs、MemCachedClient.cs。關鍵方法:

  • MemCached.GetStats(serverList, StatsCommand, param):組裝指令、呼叫客戶端、整理回傳 ArrayList
  • MemCachedClient.Stats(servers, command):建立 Socket 連線、發送指令、逐行解析回應、回傳 Hashtable

關鍵 Bug:忽略 ITEM 開頭回應

MemCachedClient.Stats 中解析迴圈原本僅處理 STAT 開頭行:

if (line.StartsWith(STATS)) // "STAT"
{
    string[] info = line.Split(' ');
    string key = info[1];
    string val = info[2];
    stats[key] = val;
}
else if (END == line) break;

但 stats cachedump 回應以 ITEM 開頭,導致所有 Key 解析遺漏。

修正程式碼

if (line.StartsWith(STATS)) // "STAT"
{
    string[] info = line.Split(' ');
    string key = info[1];
    string val = info[2];
    stats[key] = val;
}
else if (line.StartsWith("ITEM")) // 新增:處理 cachedump 回應
{
    // 格式:ITEM keyname [length b; expire_time s]
    string[] parts = line.Split('[');
    string key = parts[0].Split(' ')[1]; // 取得 keyname
    string val = "[" + parts[1];         // 保留長度與過期資訊
    stats[key] = val;
}
else if (END == line)
{
    break;
}

完整遍歷實作範例

private void GetAllCacheItems()
{
    var serverList = new ArrayList();
    foreach (string s in MemCachedManager.ServerList) serverList.Add(s);

    var allItemIds = new List();
    var sb = new StringBuilder();

    // 1. 取得所有 slab ID
    var statsResult = MemCachedManager.GetStats(serverList, MemCachedManager.Stats.Items, null);
    foreach (string line in statsResult)
    {
        var parts = line.Split(':');
        if (parts.Length > 1 && int.TryParse(parts[1], out int itemId))
        {
            if (itemId > 0 && itemId != 11211 && !allItemIds.Contains(itemId))
                allItemIds.Add(itemId);
        }
    }

    // 2. 逐一 slab 撈取 Key
    foreach (int slabId in allItemIds)
    {
        sb.AppendLine($"=== Slab {slabId} ===");
        var cacheResult = MemCachedManager.GetStats(serverList, MemCachedManager.Stats.CachedDump, $"{slabId} 100");
        foreach (string cacheLine in cacheResult)
            sb.AppendLine(cacheLine);
    }

    Response.Write(sb.ToString().Replace("\n", "
")); }

執行結果示意

修正後可正確顯示:

=== Slab 1 ===
ITEM user_session_abc123 [256 b; 1699999999 s]
ITEM product_list_456 [1024 b; 1699999999 s]
=== Slab 2 ===
ITEM order_detail_789 [512 b; 1699999999 s]

補充說明

  • 生產環境謹慎使用:cachedump 會鎖定 slab,大量資料時可能造成效能抖動,建議僅供管理工具離線使用。
  • 完整 Key 列舉替代方案:應用層自行維護 Key 索引(如 Redis Set)、或使用 memcached-tool 等外部工具。
  • DiscuzNT 版本:此 Bug 存在於 3.0 版,後續版本可能已修正,但許多專案仍基於此版本二次開發,需自行修補。
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved