STATS命令
出于性能考虑,memcached没有提供遍历功能,不过我们可以通过以下两个stats命令得到所有的缓存对象。
1、stats items
显示各个slab中item的数目。
2、stats cachedump slab_id limit_num
显示某个slab中的前limit_num个key列表,显示格式:ITEM key_name [ value_length b; expire_time|access_time s]
除了上面两个,memcached还提供了以下命令:
3、stats
4、 stats reset
5、 stats malloc
6、 stats maps
7、 stats sizes
8、 stats slabs
9、 stats detail [on|off|dump]
命令的用法就不一一说了,请自行google。 关于memcached的数据存储和内存分配以后有机会再写。
添加缓存
在本地添加几个key,如下:
程序实现
因为要用c#调用,所以需要客户端执行 STATS 命令,这个可以直接参考DiscuzNT3.0中的实现。
DiscuzNT下载地址:http://download.comsenz.com/DiscuzNT/src/
下载完程序以后,在Discuz.Cache项目中找到这两个类:MemCached.cs和MemCachedClient.cs。
我们要用到的方法有:
MemCached.GetStats

/// 获取服务器端缓存的数据信息
/// </summary>
/// <param name="serverArrayList"> 要访问的服务列表 </param>
/// <returns> 返回信息 </returns>
public static ArrayList GetStats(ArrayList serverArrayList, Stats statsCommand, string param)
{
ArrayList statsArray = new ArrayList();
param = Utils.StrIsNullOrEmpty(param) ? "" : param.Trim().ToLower();
string commandstr = " stats " ;
// 转换stats命令参数
switch (statsCommand)
{
case Stats.Reset: { commandstr = " stats reset " ; break ; }
case Stats.Malloc: { commandstr = " stats malloc " ; break ; }
case Stats.Maps: { commandstr = " stats maps " ; break ; }
case Stats.Sizes: { commandstr = " stats sizes " ; break ; }
case Stats.Slabs: { commandstr = " stats slabs " ; break ; }
case Stats.Items: { commandstr = " stats " ; break ; }
case Stats.CachedDump:
{
string [] statsparams = Utils.SplitString(param, " " );
if (statsparams.Length == 2 )
if (Utils.IsNumericArray(statsparams))
commandstr = " stats cachedump " + param;
break ;
}
case Stats.Detail:
{
if ( string .Equals(param, " on " ) || string .Equals(param, " off " ) || string .Equals(param, " dump " ))
commandstr = " stats detail " + param.Trim();
break ;
}
default : { commandstr = " stats " ; break ; }
}
// 加载返回值
Hashtable stats = MemCachedManager.CacheClient.Stats(serverArrayList, commandstr);
foreach ( string key in stats.Keys)
{
statsArray.Add(key);
Hashtable values = (Hashtable)stats[key];
foreach ( string key2 in values.Keys)
{
statsArray.Add(key2 + " : " + values[key2]);
}
}
return statsArray;
}
MemCachedClient.Stats

{
// get SockIOPool instance
SockIOPool pool = SockIOPool.GetInstance(_poolName);
// return false if unable to get SockIO obj
if (pool == null )
{
// if(log.IsErrorEnabled)
// {
// log.Error(GetLocalizedString("unable to get socket pool"));
// }
return null ;
}
// get all servers and iterate over them
if (servers == null )
servers = pool.Servers;
// if no servers, then return early
if (servers == null || servers.Count <= 0 )
{
// if(log.IsErrorEnabled)
// {
// log.Error(GetLocalizedString("stats no servers"));
// }
return null ;
}
// array of stats Hashtables
Hashtable statsMaps = new Hashtable();
for ( int i = 0 ; i < servers.Count; i ++ )
{
SockIO sock = pool.GetConnection(( string )servers[i]);
if (sock == null )
{
// if(log.IsErrorEnabled)
// {
// log.Error(GetLocalizedString("unable to connect").Replace("$$Server$$", servers[i].ToString()));
// }
continue ;
}
// build command
command = Discuz.Common.Utils.StrIsNullOrEmpty(command) ? " stats/r/n " : command + " /r/n " ;
try
{
sock.Write(UTF8Encoding.UTF8.GetBytes(command));
sock.Flush();
// map to hold key value pairs
Hashtable stats = new Hashtable();
// loop over results
while ( true )
{
string line = sock.ReadLine();
// if(log.IsDebugEnabled)
// {
// log.Debug(GetLocalizedString("stats line").Replace("$$Line$$", line));
// }
if (line.StartsWith(STATS))
{
string [] info = line.Split( ' ' );
string key = info[ 1 ];
string val = info[ 2 ];
// if(log.IsDebugEnabled)
// {
// log.Debug(GetLocalizedString("stats success").Replace("$$Key$$", key).Replace("$$Value$$", val));
// }
stats[ key ] = val;
}
else if (END == line)
{
// finish when we get end from server
// if(log.IsDebugEnabled)
// {
// log.Debug(GetLocalizedString("stats finished"));
// }
break ;
}
statsMaps[ servers[i] ] = stats;
}
}
catch // (IOException e)
{
// if(log.IsErrorEnabled)
// {
// log.Error(GetLocalizedString("stats IOException"), e);
// }
try
{
sock.TrueClose();
}
catch // (IOException)
{
// if(log.IsErrorEnabled)
// {
// log.Error(GetLocalizedString("failed to close some socket").Replace("$$Socket$$", sock.ToString()));
// }
}
sock = null ;
}
if (sock != null )
sock.Close();
}
return statsMaps;
}
有了这两个方法我们就可以得到memcached中的缓存项了。
基本思路是,先得到cache中所有的item(stats items),再通过itemid 取出cachekey和cachevalue(stats cachedump)
程序实现如下:
{
ArrayList itemarr = new ArrayList();
ArrayList arrayList = new ArrayList();
StringBuilder sb = new StringBuilder();
foreach ( string server in MemCachedManager.ServerList)
{
arrayList.Add(server);
}
ArrayList arr = MemCachedManager.GetStats(arrayList, MemCachedManager.Stats.Items, null );
foreach ( string a in arr)
{
string [] tmparr = a.Split( ' : ' );
if (tmparr.Length > 1 )
{
int item_id = 0 ;
int .TryParse(tmparr[ 1 ], out item_id);
bool find = false ;
foreach ( int item in itemarr)
{
if (item == item_id)
find = true ;
}
if ( ! find && item_id > 0 && item_id != 11211 )
itemarr.Add(item_id);
}
}
foreach ( int item in itemarr)
{
sb.Append( " item " + item + " <br /> " );
ArrayList cachearr = MemCachedManager.GetStats(arrayList, MemCachedManager.Stats.CachedDump, "" + item + " 10 " );
foreach ( string cache in cachearr)
{
sb.Append(cache);
sb.Append( " <br /> " );
}
}
Response.Write(sb.ToString());
}
运行程序:
为什么没有输出缓存项呢?
DiscuzNT3.0中的bug
于是我找啊找,发现是DiscuzNT3.0中的一个bug导致。
在MemCachedClient.Stats中,有这样的一段代码:
{
string [] info = line.Split( ' ' );
string key = info[ 1 ];
string val = info[ 2 ];
stats[ key ] = val;
}
else if (END == line)
{
break ;
}
原来是忽略了stats cachedump 的结果是以ITEM开头的,所以什么都没有输出。简单修改一下:
{
string [] info = line.Split( ' ' );
string key = info[ 1 ];
string val = info[ 2 ];
stats[ key ] = val;
}
else if (line.StartsWith( " ITEM " ))
{
string [] info = line.Split( ' [ ' );
string key = info[ 0 ].Split( ' ' )[ 1 ];
string val = " [ " + info[ 1 ];
stats[key] = val;
}
else if (END == line)
{
break ;
}
再看一下输出结果,显示正常。