<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>童城咖啡</title>
	<atom:link href="http://tank.blogs.tkiicpp.com/feed/" rel="self" type="application/rss+xml" />
	<link>http://tank.blogs.tkiicpp.com</link>
	<description>TankTong的编程生活</description>
	<lastBuildDate>Mon, 18 Apr 2011 03:39:10 +0000</lastBuildDate>
	<generator>http://wordpress.org/?v=2.9.1</generator>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
			<item>
		<title>JvmTI hello world</title>
		<link>http://tank.blogs.tkiicpp.com/2011/04/18/jvmti-hello-world/</link>
		<comments>http://tank.blogs.tkiicpp.com/2011/04/18/jvmti-hello-world/#comments</comments>
		<pubDate>Mon, 18 Apr 2011 03:39:10 +0000</pubDate>
		<dc:creator>Tank</dc:creator>
				<category><![CDATA[Life]]></category>

		<guid isPermaLink="false">http://tank.blogs.tkiicpp.com/?p=156</guid>
		<description><![CDATA[JvmTI （Jvm Tools Interface) 作为一个强大的jvm工具接口，提供很多很强大有用的功能。最近尝试下使用，简单记录下。
中文参考的是Ken Wu的博客，太具体的细节我就不累述了，这里直接解释我的hello world的过程吧。
首先需要准备一个jvm实例用于测试，为什么不选择已经在运行的象eclipse之类的大家伙，稍候介绍，我现在准备一个很简单的程序吧
public class TestMain {
    public static void main(String[] args) {
        System.out.println(&#34;Start to Sleep&#34;);
        try {
            Thread.sleep(1000 * 60 * 60);
  [...]]]></description>
		<wfw:commentRss>http://tank.blogs.tkiicpp.com/2011/04/18/jvmti-hello-world/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>如何使用RSA密钥登陆ssh</title>
		<link>http://tank.blogs.tkiicpp.com/2011/03/23/%e5%a6%82%e4%bd%95%e4%bd%bf%e7%94%a8rsa%e5%af%86%e9%92%a5%e7%99%bb%e9%99%86ssh/</link>
		<comments>http://tank.blogs.tkiicpp.com/2011/03/23/%e5%a6%82%e4%bd%95%e4%bd%bf%e7%94%a8rsa%e5%af%86%e9%92%a5%e7%99%bb%e9%99%86ssh/#comments</comments>
		<pubDate>Wed, 23 Mar 2011 09:33:09 +0000</pubDate>
		<dc:creator>Tank</dc:creator>
				<category><![CDATA[unix/linux]]></category>
		<category><![CDATA[authentication]]></category>
		<category><![CDATA[rsa]]></category>
		<category><![CDATA[ssh]]></category>

		<guid isPermaLink="false">http://tank.blogs.tkiicpp.com/?p=149</guid>
		<description><![CDATA[作为一个unix／linux小白，今天折腾了下ssh登录，使用rsa，不再需要输入密码了，具体步骤如下：

在本机（客户端）生成rsa密钥 
		
ssh-keygen -t rsa

这个命令会要提示三次输入： 
		1、第一次是存储rsa密钥的文件名称，默认的是id_rsa，可以直接回车使用默认的，也可以自己定义新的名称，最好还是放到.ssh目录下就可以了； 
		2、第二次是输入密码，最好输入一个密码，虽然可以为空（mac os下，请记住这个密码，第一次使用时需要输入） 
		3、第三次确认一次密码即可 
		生成的密钥文件有两个，以默认的为例，将会有一个id_rsa和id_rsa.pub，以.pub结尾的就是公钥，另外一个是密钥
添加rsa公钥到服务器上 
		使用scp将.pub文件复制到服务器上，使用如下命令添加新的密钥 
cat id_rsa.pub &#62;&#62; ~/.ssh/authorized_keys


		然后通过重启sshd就可以了 
/etc/init.d/sshd restart

不同平台重启命令可能不同，可以参考http://www.cyberciti.biz/faq/howto-restart-ssh/

PS: 如果在设置好了这些还是提示要密码登录，请检查ssh是否打开了rsa验证的设置，具体包括：

在/etc/ssh/sshd_config中将以下注释去掉（去掉行首的＃号）
RSAAuthentication yes
PubkeyAuthentication yes
AuthorizedKeysFile      .ssh/authorized_keys


在/etc/ssh/ssh_config中添加下面两句到host下：
Protocol 2
RSAAuthentication yes



再次重启下sshd就可以了，第一次使用rsa登录时mac os会弹出提示要输入rsa的密码，就是在创建rsa密钥时指定的。 
Enjoy!
&#160;
参考资料：http://news.softpedia.com/news/How-to-Use-RSA-Key-for-SSH-Authentication-38599.shtml
]]></description>
		<wfw:commentRss>http://tank.blogs.tkiicpp.com/2011/03/23/%e5%a6%82%e4%bd%95%e4%bd%bf%e7%94%a8rsa%e5%af%86%e9%92%a5%e7%99%bb%e9%99%86ssh/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Mac漫游记</title>
		<link>http://tank.blogs.tkiicpp.com/2011/02/17/mac%e6%bc%ab%e6%b8%b8%e8%ae%b0/</link>
		<comments>http://tank.blogs.tkiicpp.com/2011/02/17/mac%e6%bc%ab%e6%b8%b8%e8%ae%b0/#comments</comments>
		<pubDate>Thu, 17 Feb 2011 14:39:36 +0000</pubDate>
		<dc:creator>Tank</dc:creator>
				<category><![CDATA[Mac]]></category>

		<guid isPermaLink="false">http://tank.blogs.tkiicpp.com/?p=143</guid>
		<description><![CDATA[一、安装词典
&#160;&#160; &#160; &#160; &#160;为了安装一个好用的英汉词典，我找了很多方法，终于有一个文章提供的资料比较合适：http://yyq123.blogspot.com/2010/03/mac-built-in-dictionary.html，使用mac自带的dictionary已经足够了，问题是词典库哪去找，其实只要搜索stardict的词典库就可以了，可惜大多数的搜索结果都是该词典软件的，有一位好心的用户将自己找到的一些放到了网盘里了，虽然不多，已经基本够用了。
&#160;&#160; &#160; &#160; 需要注意的是，网盘里下载的是rar格式的，需要先解压，然后压缩成.tar.bz2格式，具体方式是使用terminal终端执行如下命令
tar -cvyf dict.tar.bz2 stardict-langdao-ce
&#160;&#160; &#160; &#160;第三个是要生成的压缩文件的名称，一定要写名.tar.bz2，第四个是解压后实际的词典文件夹，然后就可以通过DictUnifer来转换了。转换好的文件将被放到/Library/Dictionary目录下，所以可以备份已经转换好的词典文件，下次只需要放到这个目录下就可以使用了。打开dictionary软件后进入偏好设置就可以找到新的词典了，默认是显示的。
]]></description>
		<wfw:commentRss>http://tank.blogs.tkiicpp.com/2011/02/17/mac%e6%bc%ab%e6%b8%b8%e8%ae%b0/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Windows TO Max os x —— Svn代码提交</title>
		<link>http://tank.blogs.tkiicpp.com/2011/02/15/windows-to-max-os-x-%e2%80%94%e2%80%94-svn%e4%bb%a3%e7%a0%81%e6%8f%90%e4%ba%a4/</link>
		<comments>http://tank.blogs.tkiicpp.com/2011/02/15/windows-to-max-os-x-%e2%80%94%e2%80%94-svn%e4%bb%a3%e7%a0%81%e6%8f%90%e4%ba%a4/#comments</comments>
		<pubDate>Tue, 15 Feb 2011 08:23:37 +0000</pubDate>
		<dc:creator>Tank</dc:creator>
				<category><![CDATA[Mac]]></category>
		<category><![CDATA[Subversion]]></category>

		<guid isPermaLink="false">http://tank.blogs.tkiicpp.com/?p=137</guid>
		<description><![CDATA[&#160;&#160; &#160; &#160; &#160;迁移到Mac以后出现svn无法提交的情况，一开始以为是没有足够的权限去操作，经过努力终于以root身份登录了Mac以后还是如此。出现的错误提示如下：svn: Can&#39;t move &#39;.svn/tmp/entries&#39; to &#39;.svn/entries&#39;: Operation not permitted
&#160;&#160; &#160; &#160; &#160;ls -l查看到这个文件的操作的确是不允许root操作的，只允许拥有者进行写操作，于是尝试使用chmod修改mode，结果被拒绝：chmod: Unable to change file mode on entries: Operation not permitted， 可怜的root。。。
&#160;&#160; &#160; &#160; &#160;看来root也是由很多被限制的，经过搜索在StackOverFlow上找到了答案，原来是svn自己设置的结果，解决问题的方式是在svn的根目录下执行命令： &#160;
chflags -R nouchg .
（不要漏了&#8220;.&#8221;号）

&#160;&#160; &#160; &#160; &#160;第一次看到chflags命令，查询了下文档，原来svn将一些文件的flag设置成了uchange（immutable），所以在提交或者更新svn前需要清除这个flag，好像只有root和owner由权限，上面的命令也就是做这个的，将当前目录以及所有子目录的flag清理，这样svn就可以正常使用了。
]]></description>
		<wfw:commentRss>http://tank.blogs.tkiicpp.com/2011/02/15/windows-to-max-os-x-%e2%80%94%e2%80%94-svn%e4%bb%a3%e7%a0%81%e6%8f%90%e4%ba%a4/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>从window上移植Eclipse workspace到Mac os x历险记</title>
		<link>http://tank.blogs.tkiicpp.com/2011/02/13/eclipse-workspace%e5%8e%86%e9%99%a9%e8%ae%b0/</link>
		<comments>http://tank.blogs.tkiicpp.com/2011/02/13/eclipse-workspace%e5%8e%86%e9%99%a9%e8%ae%b0/#comments</comments>
		<pubDate>Sun, 13 Feb 2011 17:28:32 +0000</pubDate>
		<dc:creator>Tank</dc:creator>
				<category><![CDATA[Life]]></category>
		<category><![CDATA[Mac]]></category>
		<category><![CDATA[eclipse]]></category>

		<guid isPermaLink="false">http://tank.blogs.tkiicpp.com/?p=132</guid>
		<description><![CDATA[题目的确不好取，这篇仅记录下我花费了两个小时用来和eclipse的配置作的斗争。
&#160;
起源：
	
&#160;&#160; &#160; &#160; &#160; &#160;我从windows迁移到mac，本想着直接copy workspace的目录过去就完事了，可惜总有意外在等待着我们
问题：
	
&#160;&#160; &#160; &#160; &#160; &#160;由于我之前的workspace中引用了很多源代码，而源代码记录是使用windows的绝对路径来完成，所以到mac下切换了workspace的目录后就会报错，说找不到XXX文件，一看就知道是硬编码的路径地址，以C盘开头，当然找不到，但是不可能就这样放弃使用吧
解决过程：
	
&#160;&#160; &#160; &#160; &#160; 首先尝试将所有project下的.classpath文件中的source引用全部删除，然后重启eclipse，结果还在报错，经过查证是因为我引入了自定义的库（user library），所以当我添加对应资源库中的资源文件时，有部分信息一起写到user library的内容里了。
&#160;&#160; &#160; &#160; &#160; 为了找出eclipse的user library写到哪了，我翻山越岭的查看文件修改时间，隐藏目录，终于在workspace跟目录下的.metadata里找到了eclipse的启动日志.log文件，通过这个文件确定是在启动org.eclipse.jdt.core时报错的，那么接下来去哪找这个组件的配置呢？就在.metadata/.plugin/org.eclipse.core.runtime/.setting目录下，这里有很多的prefs文件，而org.eclipse.jdt.core.prefs就是配置org.eclipse.jdt.core的，果然在搜索这个文件时发现了报错的那个地址，当然要一举全部消灭，所有以userLibrary开头的记录都是自定义库的内容，其实就是xml文件的字符串，导出功能估计就是把这个写成单独的文件的。
&#160;&#160; &#160; &#160; &#160;好了，删除完毕，重启下eclipse试试吧。Yeah，没有错误了，而且在设置里的userLibrary可以正常查看了，是空的，其实如果只将源代码的引用字符串删除，这里还会保留user library的记录的。不过为以防后患，还是老老实实的重新导入一次好了。
&#160;&#160; &#160; &#160; &#160;在之前删除所有项目的源代码引用之后就可以重新导入各项目了。
总结：
&#160;&#160; &#160; &#160; &#160;第一点就是知道了eclipse如何管理workspace的相关配置的，基本上所有的配置信息会被放到workspace/.metadata/.plugin/org.eclipse.core.runtime/.setting目录下，XXX.prefs对应的就是XXX的配置了，而各workspace的日志就记在metadata目录下，所有的这些文件都是隐藏的。
&#160;&#160; &#160; &#160; &#160;第二被迫学习了几个linux命令：
&#160;&#160; &#160; &#160; &#160;&#160;ls 加上－a可以显示所有文件，加上－ct可以安装最后修改时间排序输出，－l是每行一个记录的输出，所以查看所有文件，并按最后修改时间排序的命令是： ls －actl
&#160;&#160; &#160; &#160; &#160; vim中使用ctrl ＋ b下翻一页，ctrl ＋ f上翻一页，mac没有page down和page up还真的不习惯。
&#160;&#160; &#160; &#160; &#160; 第三点其实还是教训，一开始没有直接查看日志，所以简单的以为都是project的classpath中引用了源代码，结果搞不定了反而自己不知道怎么办，幸好及时找到了日志输出，纪念下吧。晚安。
]]></description>
		<wfw:commentRss>http://tank.blogs.tkiicpp.com/2011/02/13/eclipse-workspace%e5%8e%86%e9%99%a9%e8%ae%b0/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Memcached源码阅读笔记（一）</title>
		<link>http://tank.blogs.tkiicpp.com/2010/12/31/memcached%e6%ba%90%e7%a0%81%e9%98%85%e8%af%bb%e7%ac%94%e8%ae%b0%ef%bc%88%e4%b8%80%ef%bc%89/</link>
		<comments>http://tank.blogs.tkiicpp.com/2010/12/31/memcached%e6%ba%90%e7%a0%81%e9%98%85%e8%af%bb%e7%ac%94%e8%ae%b0%ef%bc%88%e4%b8%80%ef%bc%89/#comments</comments>
		<pubDate>Fri, 31 Dec 2010 16:21:29 +0000</pubDate>
		<dc:creator>Tank</dc:creator>
				<category><![CDATA[Memcache]]></category>

		<guid isPermaLink="false">http://tank.blogs.tkiicpp.com/?p=116</guid>
		<description><![CDATA[最近开始硬着头皮阅读Memcached的源码，记录下发现的细节和学习到小技巧吧，由于我自己很久没有写过c了，还随便复习了下c的基础知识。
1、第一个slab的chunk size值
&#160;&#160; &#160; &#160; 当我试图修改memcached的起始slab大小以便用于存储非常小的缓存对象（往往值是没有意义的，只是用一个1之类的标志来表示key是否存在，这个通常用于判断缓存一种状态而不是数值，例如是否获得过当天的登陆奖励）时发现了这个问题。
	&#160;&#160; &#160; &#160; &#160;通过启动memcached时添加参数: -n48 （48是要被设置的值，必须是正数） 可以指定第一个slab的chunk大小，但是经过尝试和跟踪代码发现，第一个slab的chunk size不止受此一个参数限制，具体计算第一个chunk size的过程如下：

-n 指定的值被存放到&#160;settings.chunk_size中
当memcached启动时，会初始化slab（Slabs.c中的slabs_init），第一个slab的chunk size大小首先会被指定为
		unsigned int size = sizeof(item) + settings.chunk_size;
		然后在实际确定前需要转换为8的整数倍
		if (size % CHUNK_ALIGN_BYTES)
		&#160;&#160;&#160;&#160;size += CHUNK_ALIGN_BYTES &#8211; (size % CHUNK_ALIGN_BYTES);

&#160;&#160; &#160; &#160; &#160;通过上面的计算过程可以发现，其实第一个slab的chunk size还受到struct item的大小决定。
	&#160;&#160; &#160; &#160; &#160;在尝试使用最小值时（即指定-n1时），在我的机器32位机器上生成的第一个slab大小是48，原因是 sizeof(item) 为32，加上1以后33，而33不是8的倍数，所以放大到48；在64位机器上 sizeof(item) 为48，所以最小应该为 56。
2、chunk中不仅仅存放缓存对象的value
&#160;&#160; &#160; &#160; &#160; 这个不知道是我自己理解的错误还是误听哪的说明，我一直以为chunk中只存放了缓存对象的value，所以在分配slab时只需要根据缓存的value大小来。但是这种情况下无法解释为什么在确定slab的chunk大小时要加上一个item的大小，如果只放1byte的数据却使用的是56byte（64位的服务器）的chunk未免太浪费了，而且不可能没有人发现这种情况，所以有理由怀疑我以前的认识了。
	&#160;&#160; &#160;chunk中不仅保持了缓存对象的value，而且保存了缓存对象的key，expire time， flag等详细信息
	&#160;&#160; &#160; &#160; &#160; 经过查看memcached的源代码发现两个问题：

memcached如何计算需要存放的缓存对象大小，以及如何选择slab
		查找缓存对象应该放到哪个slab时，不仅适用了value的长度，而且使用很多其他信息，具体计算公式如下：
		&#160;&#160; &#160;*nsuffix [...]]]></description>
		<wfw:commentRss>http://tank.blogs.tkiicpp.com/2010/12/31/memcached%e6%ba%90%e7%a0%81%e9%98%85%e8%af%bb%e7%ac%94%e8%ae%b0%ef%bc%88%e4%b8%80%ef%bc%89/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>导出Memcached所有缓存数据</title>
		<link>http://tank.blogs.tkiicpp.com/2010/12/16/%e5%af%bc%e5%87%bamemcached%e6%89%80%e6%9c%89%e7%bc%93%e5%ad%98%e6%95%b0%e6%8d%ae/</link>
		<comments>http://tank.blogs.tkiicpp.com/2010/12/16/%e5%af%bc%e5%87%bamemcached%e6%89%80%e6%9c%89%e7%bc%93%e5%ad%98%e6%95%b0%e6%8d%ae/#comments</comments>
		<pubDate>Thu, 16 Dec 2010 13:40:23 +0000</pubDate>
		<dc:creator>Tank</dc:creator>
				<category><![CDATA[Memcache]]></category>
		<category><![CDATA[memcached]]></category>

		<guid isPermaLink="false">http://tank.blogs.tkiicpp.com/?p=108</guid>
		<description><![CDATA[关于导出memcached的全部数据，只是使用了memcached提供的几个功能接口而已，具体代码如下，但是有几个要注意的地方。

第一个是cachedump命令每次返回的数据大小只有2M，这个是memcached的代码中写死的一个数值，除非在编译前修改（见参考一）
第二个问题是在一个memcached做cachedump时，似乎不能在循环里面对另外一个memcached做set操作。这个只是在尝试的时候发现的问题，一开始我是在第一个foreach循环中获取数据并放到另外一个memcached里的，这个时候会出现放置的数据为2byte的一个假数据。经过测试，取的数据时对的，但是放的时候出现了错误，所以下面的代码先循环cachedump的key，再单独循环key列表去导出数据。
最后这个问题是有第一个引起的，根据我导入的情况看，2M一般只能拿到4万左右的key，有些slab的item达到了4千万，4万只是千分之一，所以只能边导出边删除，知道没有数据可以导出为止。

&#60;?php
echo &#34;Start time &#34;.date(&#34;Y-m-d H:i:s&#34;).&#34;\n&#34;;
$slabs = array_slice($argv, 1);

foreach ($slabs as $slabId) {
    export($slabId);
}

echo &#34;End time &#34;.date(&#34;Y-m-d H:i:s&#34;).&#34;\n&#34;;

function export($slabId)
{
        $num = 0;
        $total = 0;
        $times = 0;
     [...]]]></description>
		<wfw:commentRss>http://tank.blogs.tkiicpp.com/2010/12/16/%e5%af%bc%e5%87%bamemcached%e6%89%80%e6%9c%89%e7%bc%93%e5%ad%98%e6%95%b0%e6%8d%ae/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Memcached性能检测</title>
		<link>http://tank.blogs.tkiicpp.com/2010/12/16/memcached%e6%80%a7%e8%83%bd%e6%a3%80%e6%b5%8b/</link>
		<comments>http://tank.blogs.tkiicpp.com/2010/12/16/memcached%e6%80%a7%e8%83%bd%e6%a3%80%e6%b5%8b/#comments</comments>
		<pubDate>Thu, 16 Dec 2010 10:54:07 +0000</pubDate>
		<dc:creator>Tank</dc:creator>
				<category><![CDATA[Memcache]]></category>
		<category><![CDATA[memcached]]></category>
		<category><![CDATA[performance]]></category>

		<guid isPermaLink="false">http://tank.blogs.tkiicpp.com/?p=93</guid>
		<description><![CDATA[上篇解释了Memcached的内存分配机制，这篇总结下如何检测memcached是否发挥了优秀的性能。
二、Memcached性能检测
&#160;&#160; &#160; &#160; &#160;Memcached作为一个内存key-value存储容器有非常优秀的性能，但是在上次的使用中确发现大量的数据丢失情况发生，导致cache的功能基本消失。具体的检测方式如下：

检测命中率
检测命中率是一个最基本的、最宏观的方式，使用telnet连接到memcached服务器，然后执行stats命令就可以看到宏观的一些信息，如下图。
			
			&#160;&#160; &#160; &#160; &#160;这个命令中比较关键的属性是get_hits和get_misses，get_hits表示读取cache命中的次数，get_misses是读取失败的次数，即尝试读取不存在的缓存数据。
			&#160;&#160; &#160; &#160; &#160; 命中率=get_hits / (get_hits + get_misses)
			命中率越高说明cache起到的缓存作用越大。但是在实际使用中，这个命中率不是有效数据的命中率，有些时候get操作可能只是检查一个key存在不存在，这个时候miss也是正确的，这就像用memcached作为一种定时器，将一些临时数据在memcache中存放特定时间长度，业务逻辑会根据cache是否存在而作不同的逻辑，这种数据其实已经不是单纯的缓存了，也不应该统计到命中率中。再者，这个命中率是从memcached启动开始所有的请求的综合值，不能反映一个时间段内的情况，所以要排查memcached的性能问题，还需要更详细的数值。但是高的命中率还是能够反映出memcached良好的使用情况，突然下跌的命中率能够反映大量cache丢失的发生。

Stats items
Stats items命令可以查看每个slab中存储的item的一些详细信息，具体可以见下图。
			
			关键属性有：

Stats items属性


属性名称
属性说明


number
存放的数据总数


age
存放的数据中存放时间最久的数据已经存在的时间，以秒为单位


evicted
被剔除的数据总数


evicted_time
最后被剔除的数据在cache中存放的时间，以秒为单位



stats items可以详细的观察各slab的数据对象的情况，因为memcached的内存分配策略导致一旦memcached的总内存达到了设置的最大内存，代表所有的slab能够使用的page都已经固定，这个时候如果还有数据放入，将开始导致memcached使用LRU策略剔除数据。而LRU策略不是针对所有的slabs，而是只针对新数据应该被放入的slab，例如有一个新的数据要被放入slab 3，则LRU只对slab 3进行。通过stats items就可以观察到这些剔除的情况。
			具体分析如下：

evicted属性
				如果一个slab的evicted属性不是0，则说明当前slab出现了提前剔除数据的情况，这个slab可能是你需要注意的。
evicted_time属性
				如果evicted不为0，则evicited_time就代表最后被剔除的数据时间缓存的时间。并不是发生了LRU就代码memcached负载过载了，因为有些时候在使用cache时会设置过期时间为0，这样缓存将被存放30天，如果内存慢了还持续放入数据，而这些为过期的数据很久没有被使用，则可能被剔除。需要注意的是，最后剔除的这个数据已经被缓存的时间，把evicted_time换算成标准时间看下是否已经达到了你可以接受的时间，例如：你认为数据被缓存了2天是你可以接受的，而最后被剔除的数据已经存放了3天以上，则可以认为这个slab的压力其实可以接受的；但是如果最后被剔除的数据只被缓存了20秒，不用考虑，这个slab已经负载过重了。
age属性
				age属性反应了当前还在缓存的数据中最久的时间，它的大小和evicted_time没有必然的大小关系，因为可能时间最久的数据确实频繁被读取的，这时候不会被LRU清理掉，但是如果它小于evicted_time的话，则说明数据在被下去读取前就被清理了，或者存放了很多长时间但是不被使用的缓存对象。


Stats slabs
从Stats items中如果发现有异常的slab，则可以通过stats slabs查看下该slab是不是内存分配的确有问题。
			Stats slabs结果如下图
			
			Stats slabs的属性说明如下：

Stats slabs属性


属性名称
属性说明


chunk_size
当前slab每个chunk的大小


chunk_per_page
每个page能够存放的chunk数


total_pages
分配给当前slab的page总数


total_chunks
当前slab最多能够存放的chunk数，应该等于chunck_per_page * total_page


used_chunks
已经被占用的chunks总数


free_chunks
过期数据空出的chunk里还没有被使用的chunk数


free_chunks_end
新分配的但是还没有被使用的chunk数




		这个命令的信息量很大，所有属性都很有价值。下面一一解释各属性：
综合上面的数据，可以发现造成memcached的内存使用率降低的属性有：

chunk_size, chunk_per_page
				这两个属性是固定的，但是它反映当前slab存储的数据大小，可以供你分析缓存数据的散列区间，通过调整增长因子可以改变slab的区间分布，从而改变数据散列到的区域。如果大量的230byte到260byte的数据，而刚好一个slab大小是250byte，则250byte到260byte的数据将被落到下一个slab，从而导致大量的空间浪费。
total_pages
				这个是当前slab总共分配大的page总数，如果没有修改page的默认大小的情况下，这个数值就是当前slab能够缓存的数据的总大小（单位为M）。如果这个slab的剔除非常严重，一定要注意这个slab的page数是不是太少了。
				我上次处理的那个项目因为和另外的一个项目共用的memcache，而且memcache已经运行了很长时间，导致page都已经全部被分配完，而刚好两个项目的缓存数据大小差别很多，导致新项目数据最多的slab 4竟然只有一个page，所以数据缓存不到22s就被替换了，完全失去了缓存的意义。
				针对我遇到的那个情况，解决方案是重新分配page，或者重启memcache服务。但是page reassign方法从1.2.8版已经完全移除了，所以现在没有办法在线情况下重新分配page了。另外一种有些时候是不可以接受的，因为一次缓存服务器的重启将导致所有缓存的数据将重新从DB取出，这个可能造成db的压力瞬间增大。而且有的缓存数据时不入库的，这个时候我们就需要做memcache的导入和导出了。在下篇文章中我会总结下memcache的dump操作。
total_chunks
				这个的作用和total_pages基本相同，不过这个属性可以更准确的反应实际可以存放的缓存对象总数。
used_chunks, free_chunks, free_chunks_end
				这三个属性相关度比较高，从数值上来看它们满足： 
				&#160;&#160; &#160; &#160; &#160; &#160; &#160; &#160; &#160;total_chunks = used_chunks + free_chunks + free_chunks_end
				used_chunks就是字面的意思，已经使用的chunk数；free_chunks却不是所有的未被使用的chunk数，而是曾经被使用过但是因为过期而被回收的chunk数；free_chunks_end是page中从来没有被使用过的chunk数。
				
				&#160;&#160; &#160; &#160;从上图可以看出，slab 1只放了一个对象，但是已经申请了一整个page，这个时候used_chunks为1，但是free_chunks却为0，因为还没有任何回收的空间，而free_chunks_end却等于10081，说明这么多的chunk从来没有被使用过。下图就是这个数据过期后的stats slabs数据，可以发现free_chunks有值了，就是过期的那个chunk，所以是1，used_chunks为0，free_chunks_end不变。
				
				&#160;&#160; &#160; &#160;为什么要分两种free [...]]]></description>
		<wfw:commentRss>http://tank.blogs.tkiicpp.com/2010/12/16/memcached%e6%80%a7%e8%83%bd%e6%a3%80%e6%b5%8b/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Memcache内存分配策略</title>
		<link>http://tank.blogs.tkiicpp.com/2010/12/14/memcache%e5%86%85%e5%ad%98%e5%88%86%e9%85%8d%e7%ad%96%e7%95%a5/</link>
		<comments>http://tank.blogs.tkiicpp.com/2010/12/14/memcache%e5%86%85%e5%ad%98%e5%88%86%e9%85%8d%e7%ad%96%e7%95%a5/#comments</comments>
		<pubDate>Tue, 14 Dec 2010 09:17:01 +0000</pubDate>
		<dc:creator>Tank</dc:creator>
				<category><![CDATA[Memcache]]></category>
		<category><![CDATA[memcached]]></category>

		<guid isPermaLink="false">http://tank.blogs.tkiicpp.com/?p=79</guid>
		<description><![CDATA[上周由于接手个一个新的项目，该项目对于memcache的依赖非常大，从而导致我不得不真的开始深入了解memcache的内存使用情况，这里总结下我个人的收获，也算是一次小的memcache优化吧。
一、Memcache内存分配机制
&#160;&#160; &#160; &#160; &#160;关于这个机制网上有很多解释的，我个人的总结如下。

Page为内存分配的最小单位。
Memcached的内存分配以page为单位，默认情况下一个page是1M，可以通过-I参数在启动时指定。如果需要申请内存时，memcached会划分出一个新的page并分配给需要的slab区域。page一旦被分配在重启前不会被回收或者重新分配（page ressign已经从1.2.8版移除了） 
			

Slabs划分数据空间。
Memcached并不是将所有大小的数据都放在一起的，而是预先将数据空间划分为一系列slabs，每个slab只负责一定范围内的数据存储。如下图，每个slab只存储大于其上一个slab的size并小于或者等于自己最大size的数据。例如：slab 3只存储大小介于137 到 224 bytes的数据。如果一个数据大小为230byte将被分配到slab 4中。从下图可以看出，每个slab负责的空间其实是不等的，memcached默认情况下下一个slab的最大值为前一个的1.25倍，这个可以通过修改-f参数来修改增长比例。 
			

Chunk才是存放缓存数据的单位。
Chunk是一系列固定的内存空间，这个大小就是管理它的slab的最大存放大小。例如：slab 1的所有chunk都是104byte，而slab 4的所有chunk都是280byte。chunk是memcached实际存放缓存数据的地方，因为chunk的大小固定为slab能够存放的最大值，所以所有分配给当前slab的数据都可以被chunk存下。如果时间的数据大小小于chunk的大小，空余的空间将会被闲置，这个是为了防止内存碎片而设计的。例如下图，chunk size是224byte，而存储的数据只有200byte，剩下的24byte将被闲置。 
			

Slab的内存分配。
Memcached在启动时通过-m指定最大使用内存，但是这个不会一启动就占用，是随着需要逐步分配给各slab的。
			&#160;&#160; &#160; &#160; &#160; 如果一个新的缓存数据要被存放，memcached首先选择一个合适的slab，然后查看该slab是否还有空闲的chunk，如果有则直接存放进去；如果没有则要进行申请。slab申请内存时以page为单位，所以在放入第一个数据，无论大小为多少，都会有1M大小的page被分配给该slab。申请到page后，slab会将这个page的内存按chunk的大小进行切分，这样就变成了一个chunk的数组，在从这个chunk数组中选择一个用于存储数据。如下图，slab 1和slab 2都分配了一个page，并按各自的大小切分成chunk数组。 
			

Memcached内存分配策略。
综合上面的介绍，memcached的内存分配策略就是：按slab需求分配page，各slab按需使用chunk存储。
			这里有几个特点要注意，

Memcached分配出去的page不会被回收或者重新分配
Memcached申请的内存不会被释放
slab空闲的chunk不会借给任何其他slab使用




&#160;
&#160;&#160; &#160; &#160;知道了这些以后，就可以理解为什么总内存没有被全部占用的情况下，memcached却出现了丢失缓存数据的问题了。
关于memcached命令行参数可以参考：http://techgurulive.com/2010/01/26/how-to-configure-memcached-memcached-configuration-parameters/
]]></description>
		<wfw:commentRss>http://tank.blogs.tkiicpp.com/2010/12/14/memcache%e5%86%85%e5%ad%98%e5%88%86%e9%85%8d%e7%ad%96%e7%95%a5/feed/</wfw:commentRss>
		<slash:comments>3</slash:comments>
		</item>
		<item>
		<title>通过设置公共父域名的cookie达到垮子域名的效果</title>
		<link>http://tank.blogs.tkiicpp.com/2010/12/13/%e9%80%9a%e8%bf%87%e8%ae%be%e7%bd%ae%e5%85%ac%e5%85%b1%e7%88%b6%e5%9f%9f%e5%90%8d%e7%9a%84cookie%e8%be%be%e5%88%b0%e5%9e%ae%e5%ad%90%e5%9f%9f%e5%90%8d%e7%9a%84%e6%95%88%e6%9e%9c/</link>
		<comments>http://tank.blogs.tkiicpp.com/2010/12/13/%e9%80%9a%e8%bf%87%e8%ae%be%e7%bd%ae%e5%85%ac%e5%85%b1%e7%88%b6%e5%9f%9f%e5%90%8d%e7%9a%84cookie%e8%be%be%e5%88%b0%e5%9e%ae%e5%ad%90%e5%9f%9f%e5%90%8d%e7%9a%84%e6%95%88%e6%9e%9c/#comments</comments>
		<pubDate>Mon, 13 Dec 2010 07:45:35 +0000</pubDate>
		<dc:creator>Tank</dc:creator>
				<category><![CDATA[Java]]></category>
		<category><![CDATA[web]]></category>

		<guid isPermaLink="false">http://tank.blogs.tkiicpp.com/?p=81</guid>
		<description><![CDATA[&#160;&#160; &#160; &#160; &#160;今天为了能够成功访问公司另外一个部门的资源，不得不跨域设置cookie，因为对方使用了cookie做身份验证，否则请求会被拒绝。解决方案如下：
&#160;&#160; &#160; &#160; 假设我现在使用的域名是 tank.tkiicpp.com，而我需要访问 subdomain.tkiicpp.com的一个资源，并且需要在cookie里指定一个特殊值以标识身份，例如： vendor = tkiicpp，这样才能通过 subdomain.tkiicpp.com 的身份验证，这个时候出现了一个问题，我必须在 tank.tkiicpp.com 域下设置一个 cookie 能够在 subdomain.tkiicpp.com 域使用。幸好这两个域名都是　tkiicpp.com 的子域名，这个就是解决这个问题的关键，通过设置一个cookie到父域名( tkiicpp.com ) 下，在访问 subdomain.tkiicpp.com 的时候，这个cookie会被默认带上，java代码如下：
        Cookie cookie = new Cookie(&#34;vendor&#34;, &#34;tkiicpp&#34;);
        //设置为-1指定当前cookie不持久化，将随浏览器的关闭而被清理
        cookie.setMaxAge(-1);
 [...]]]></description>
		<wfw:commentRss>http://tank.blogs.tkiicpp.com/2010/12/13/%e9%80%9a%e8%bf%87%e8%ae%be%e7%bd%ae%e5%85%ac%e5%85%b1%e7%88%b6%e5%9f%9f%e5%90%8d%e7%9a%84cookie%e8%be%be%e5%88%b0%e5%9e%ae%e5%ad%90%e5%9f%9f%e5%90%8d%e7%9a%84%e6%95%88%e6%9e%9c/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
	</channel>
</rss>

