Memcached 為高效能分散式快取系統,但出於效能考量,不提供原生遍歷/列舉所有 Key 的指令。若需實作「查看所有快取內容」或「依條件清除」等管理功能,必須透過 stats 指令間接達成。本文完整記錄從 stats items 取得 slab ID,再用 stats cachedump 列出 Key,並修正 DiscuzNT 3.0 客戶端無法解析 ITEM 開頭回應的 Bug。
| 指令 | 用途 | 回應格式範例 |
|---|---|---|
stats items | 顯示各 slab 中 item 數量 | STAT items:1:number 50 |
stats cachedump <slab_id> <limit> | 列出指定 slab 前 N 個 Key | ITEM mykey [123 b; 1234567890 s] |
stats | 一般統計資訊 | STAT pid 1234 |
stats slabs | Slab 分配細節 | ... |
專案引用 Discuz.Cache 專案中的 MemCached.cs、MemCachedClient.cs。關鍵方法:
MemCached.GetStats(serverList, StatsCommand, param):組裝指令、呼叫客戶端、整理回傳 ArrayListMemCachedClient.Stats(servers, command):建立 Socket 連線、發送指令、逐行解析回應、回傳 HashtableMemCachedClient.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,大量資料時可能造成效能抖動,建議僅供管理工具離線使用。memcached-tool 等外部工具。