<?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>Tue, 08 May 2012 09:40:30 +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>Git tutorial</title>
		<link>http://tank.blogs.tkiicpp.com/2012/04/17/git-tutorial/</link>
		<comments>http://tank.blogs.tkiicpp.com/2012/04/17/git-tutorial/#comments</comments>
		<pubDate>Tue, 17 Apr 2012 08:32:04 +0000</pubDate>
		<dc:creator>Tank</dc:creator>
				<category><![CDATA[Git]]></category>
		<category><![CDATA[tutorial]]></category>

		<guid isPermaLink="false">http://tank.blogs.tkiicpp.com/?p=169</guid>
		<description><![CDATA[If you want to start git the new version control system, take a look at this page&#160;http://rogerdudler.github.com/git-guide/ which is really good for beginning.
I made some notes for myself:

Create a git repository: &#160;
mkdir git_repo
cd git_repo
git init
This will create an empty repository in folder git_repo
Clone a git repository to local
git clone /path_to_repo (such as /data/git_repo)

This will clone [...]]]></description>
		<wfw:commentRss>http://tank.blogs.tkiicpp.com/2012/04/17/git-tutorial/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>My tool kit</title>
		<link>http://tank.blogs.tkiicpp.com/2012/03/23/my-tool-kit/</link>
		<comments>http://tank.blogs.tkiicpp.com/2012/03/23/my-tool-kit/#comments</comments>
		<pubDate>Fri, 23 Mar 2012 07:18:28 +0000</pubDate>
		<dc:creator>Tank</dc:creator>
				<category><![CDATA[Life]]></category>

		<guid isPermaLink="false">http://tank.blogs.tkiicpp.com/?p=165</guid>
		<description><![CDATA[Just make a list to remember

Develop language: Java
Develop frameworks: Felix, maven-pax-plugin&#160;, ipojo
Source code version control: svn, git
Build tool: Maven
Continuous integration tool: Hudson
Mysql DRM ide: mysql work bench&#160;(here are more alternatives for power designer : http://alternativeto.net/software/sybase-power-designer/)
UI flow&#160;axrue:&#160;http://www.axure.com/

&#160;
TO BE COMTINUE&#8230;
]]></description>
		<wfw:commentRss>http://tank.blogs.tkiicpp.com/2012/03/23/my-tool-kit/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<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>
	</channel>
</rss>

