<?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>服务器运维与网站架构&#124;Linux运维&#124;互联网研究</title>
	<atom:link href="http://www.ha97.com/feed" rel="self" type="application/rss+xml" />
	<link>http://www.ha97.com</link>
	<description>记录我的思想轨迹、工作学习、生活和关注的领域</description>
	<lastBuildDate>Tue, 15 May 2012 13:49:54 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.2.1</generator>
		<item>
		<title>（原创）Nginx升级到1.2.0版本后出现的报错nginx: [warn] the “log_format” directive may be used only on “http” level解决方法</title>
		<link>http://www.ha97.com/4607.html</link>
		<comments>http://www.ha97.com/4607.html#comments</comments>
		<pubDate>Tue, 15 May 2012 13:32:20 +0000</pubDate>
		<dc:creator>谋万世全局者</dc:creator>
				<category><![CDATA[Linux]]></category>
		<category><![CDATA[Nginx]]></category>
		<category><![CDATA[UNIX]]></category>
		<category><![CDATA[Web Server]]></category>
		<category><![CDATA[个人日记]]></category>
		<category><![CDATA[原创总结]]></category>
		<category><![CDATA[http]]></category>
		<category><![CDATA[log_format]]></category>
		<category><![CDATA[warn]]></category>
		<category><![CDATA[升级]]></category>
		<category><![CDATA[原创]]></category>
		<category><![CDATA[报错]]></category>
		<category><![CDATA[解决方法]]></category>

		<guid isPermaLink="false">http://www.ha97.com/?p=4607</guid>
		<description><![CDATA[PS：近来工作忙，很久没上去本博的VPS，刚才找空上去整理一下，顺便update一些软件包，目前Nginx的最新稳定版本是1.2.0，升级到这个版本后，重启nginx出现如下报错： 根据报错信息，意思是这段配置： log_format access '$remote_addr - $remote_user [$time_local] "$request" ' '$status $body_bytes_sent "$http_referer" ' '"$http_user_agent" $http_x_forwarded_for'; 只能放到http{}里面，而不能放到server{}里面。为啥会这样？只有一个解释，就是nginx的作者在这个版本把这段日志格式配置的语法做了修改！ 解决方法： 把上面log_format的日志格式配置移出server{}放在http{}里面，但要在server{}的外面。这样就OK。如果你的配置里面还有类似： access_log /var/log/nginx/access.log access; 这样的日志文件路径配置，也跟着一样迁移！否则还会报错！ 问题完全解决，如下图： 最后说一句：生产环境升级软件包要谨慎，必须要经过严格测试。我这个VPS自己玩的，折腾没事。]]></description>
			<content:encoded><![CDATA[<p><strong>PS：近来工作忙，很久没上去本博的VPS，刚才找空上去整理一下，顺便update一些软件包，目前Nginx的最新稳定版本是1.2.0，升级到这个版本后，重启nginx出现如下报错：</strong><br />
<a href="http://www.ha97.com/wp-content/uploads/2012/05/log_format1.jpg"><img class="alignnone size-full wp-image-4610" title="log_format1" src="http://www.ha97.com/wp-content/uploads/2012/05/log_format1.jpg" alt="" width="972" height="140" /></a></p>
<p>根据报错信息，意思是这段配置：<br />
<code><br />
log_format access '$remote_addr - $remote_user [$time_local] "$request" '<br />
'$status $body_bytes_sent "$http_referer" '<br />
'"$http_user_agent" $http_x_forwarded_for';<br />
</code><br />
<strong>只能放到http{}里面，而不能放到server{}里面。为啥会这样？只有一个解释，就是nginx的作者在这个版本把这段日志格式配置的语法做了修改！</strong></p>
<p><strong>解决方法：</strong><br />
<span id="more-4607"></span><br />
把上面log_format的日志格式配置移出server{}放在http{}里面，但要在server{}的外面。这样就OK。如果你的配置里面还有类似：<br />
<code>access_log /var/log/nginx/access.log access;</code><br />
这样的日志文件路径配置，也跟着一样迁移！否则还会报错！</p>
<p>问题完全解决，如下图：<br />
<a href="http://www.ha97.com/wp-content/uploads/2012/05/log_format2.jpg"><img class="alignnone size-full wp-image-4611" title="log_format2" src="http://www.ha97.com/wp-content/uploads/2012/05/log_format2.jpg" alt="" width="767" height="120" /></a></p>
<p><span style="color: #ff0000;">最后说一句：生产环境升级软件包要谨慎，必须要经过严格测试。我这个VPS自己玩的，折腾没事。</span></p>
]]></content:encoded>
			<wfw:commentRss>http://www.ha97.com/4607.html/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>IBM设计史上最强超级计算机：吞吐数据量等于全球互联网两倍</title>
		<link>http://www.ha97.com/4590.html</link>
		<comments>http://www.ha97.com/4590.html#comments</comments>
		<pubDate>Mon, 14 May 2012 03:31:04 +0000</pubDate>
		<dc:creator>谋万世全局者</dc:creator>
				<category><![CDATA[互联网]]></category>
		<category><![CDATA[科技资讯]]></category>
		<category><![CDATA[IBM]]></category>
		<category><![CDATA[全球互联网]]></category>
		<category><![CDATA[史上最强]]></category>
		<category><![CDATA[吞吐数据量]]></category>
		<category><![CDATA[超级计算机]]></category>

		<guid isPermaLink="false">http://www.ha97.com/?p=4590</guid>
		<description><![CDATA[IBM公司正在设计史上最强的计算机。这台计算机将与占地1900平方英里（约合4920平方公里）的平方公里阵列相连，负责分析望远镜获取的数据。平方公里阵列能够观测到130亿年前的宇宙景象，当时刚发生大爆炸不久，正是宇宙的黎明时期。 平方公里阵列产生的数据相当于整个互联网流量的两倍，大型强子对撞机的100倍 在道格拉斯-亚当斯的《银河系漫游指南》中，一个外星文明制造了一台巨型计算机，用于揭开宇宙的一系列谜团。不幸的是，这台计算机给出的答案是“42” 平方公里阵列由数千个碟形天线构成，用于收集射电信号，天线覆盖面积超过1平方公里，整个阵列的宽度相当于美国大陆 IBM正在研制3D芯片，用以达到平方公里阵列需要的运算能力 艺术概念图，展示了平方公里阵列的碟形天线。这个望远镜阵列用于观测宇宙中的电磁辐射，揭开宇宙的一系列谜团 平方公里阵列的碟形天线拥有不可思议的灵敏度，能够发现距地球50光年的一颗行星上的机场雷达 新浪科技讯 北京时间4月6日消息，据国外媒体报道，美国IBM公司正在设计史上最强的计算机，每天的数据吞吐数量相当于整个互联网，可用于对来自太空的无线电波进行 分析，探索宇宙起源的奥秘。这台计算机将与占地1900平方英里(约合4920平方公里)的望远镜阵列——平方公里阵列相连，负责分析望远镜获取的数据。 平方公里阵列能够观测到130亿年前的宇宙景象，当时刚发生大爆炸不久，正是宇宙的黎明时期。 IBM设计的最强计算机运算能力是当前速度最快的PC的数百万倍，负责处理的数据数量将达到大型强子对撞机所产生数据的100倍。IBM研发部 门的汤恩-恩波森表示：“平方公里阵列每天获取的数据相当于当前全球每天互联网流量的两倍。”据悉，平方公里阵列将于2024年竣工，能够观测到130亿 年前的宇宙景象，将用于研究星系演化、暗物质以及宇宙的起源。 IBM将采用3D芯片制造这台计算机，用以达到平方公里阵列需要的运算能力。平方公里阵列将安装数千个碟形天线，天线覆盖面积超过1平方公里， 整个阵列的宽度相当于美国大陆。这个望远镜阵列的灵敏度是此前任何射电望远镜的50倍，速度是当前望远镜的1万倍。平方公里阵列每平方公里每天产生的数据 估计可达到数EB(艾字节)。经过处理后，每年存储的数据估计在300到1500PB(拍字节)之间。相比之下，欧洲核子研究组织的大型强子对撞机每年产 生的数据在15 PB左右。 4月3日，平方公里阵列项目负责人在阿姆斯特丹举行会议，商讨这个巨型望远镜阵列的具体建造地点。据悉，这个阵列的占地面积将达到1900平方 英里，预计于2016年动工建造。平方公里阵列项目临时总负责人迈克尔-范-哈勒姆说：“这个望远镜阵列将对我们了解地球在宇宙中的位置产生深远影响，同 时有助于加深我们对宇宙的历史和未来的了解。我们相信未来将上演一系列重大发现。” 平方公里阵列的建造地点仍是一个未知数 中频孔径阵列，可一次对多个太空区域进行观测 艺术概念图，展示了平方公里阵列的低频孔径阵列 一幅图解，展示了平方公里阵列的碟形天线和孔径阵列 借助于平方公里阵列，科学家将加深对黑洞以及恒星如何诞生的了解，同时也可确定太阳系外是否有生命存在 平方公里阵列的原型KAT-7，目前正在南非卡鲁沙漠接受测试 平方公里阵列将由数千个碟形天线构成，天线总面积达到1平方公里，能够产生数量惊人的数据，彻底改变我们对宇宙的认知。英国科学探索中心协会的 伊恩-格里芬博士表示：“平方公里阵列项目将为天文学家提供一个全新的工具，让我们对宇宙的认知发生革命性变化。这个阵列能够对面积巨大的太空区域进行观 测，呈现难以置信的星系细节，有助于科学家研究神秘而令人兴奋的黑洞等天体，进而验证相对论，同时也允许天文学家进一步了解宇宙的早期历史。” 科学界认为他们可以借助平方公里阵列确定太阳系外是否有生命存在。搜寻系外生命需要使用具有突破性的技术。平方公里阵列的碟形天线直径15米， 能够探测天体放射出的电磁辐射。它们拥有不可思议的灵敏度，能够发现距地球50光年的一颗行星上的机场雷达。碟形天线将在低频和中频孔径阵列的协助下进行 观测，这些阵列能够让平方公里阵列拥有广阔的视野，一次可对多个太空区域进行观测。 平方公里阵列使用的光缆长度惊人，可绕地球两周。目前，这一望远镜阵列的建造地点仍是一个未知数，非洲南部和澳大利亚均在考虑范围之内。这两个 地区都拥有适于望远镜进行观测的环境。望远镜必须在没有电子干扰——例如手机产生的干扰——的情况下才能获得最清晰的“视线”。平方公里阵列预计于 2016年动工，碟形天线将在8年后投入使用。这一阵列的原型KAT-7由7个直径16米的碟形天线构成，目前正在南非卡鲁沙漠接受测试。 转自：http://www.oschina.net/news/27590/ibm-power-computer]]></description>
			<content:encoded><![CDATA[<p><img title="IBM公司正在设计史上最强的计算机。这台计算机将与占地1900平方英里（约合4920平方公里）的平方公里阵列相连，负责分析望远镜获取的数据。平方公里阵列能够观测到130亿年前的宇宙景象，当时刚发生大爆炸不久，正是宇宙的黎明时期" src="http://www.ha97.com/wp-content/uploads/image/2012/05/03320382M.jpg" alt="IBM公司正在设计史上最强的计算机。这台计算机将与占地1900平方英里（约合4920平方公里）的平方公里阵列相连，负责分析望远镜获取的数据。平方公里阵列能够观测到130亿年前的宇宙景象，当时刚发生大爆炸不久，正是宇宙的黎明时期" /><br />
IBM公司正在设计史上最强的计算机。这台计算机将与占地1900平方英里（约合4920平方公里）的平方公里阵列相连，负责分析望远镜获取的数据。平方公里阵列能够观测到130亿年前的宇宙景象，当时刚发生大爆炸不久，正是宇宙的黎明时期。</p>
<p><span id="more-4590"></span></p>
<p><img title="平方公里阵列产生的数据相当于整个互联网流量的两倍，大型强子对撞机的100倍" src="http://www.ha97.com/wp-content/uploads/image/2012/05/033205e5T.jpg" alt="平方公里阵列产生的数据相当于整个互联网流量的两倍，大型强子对撞机的100倍" /><br />
平方公里阵列产生的数据相当于整个互联网流量的两倍，大型强子对撞机的100倍</p>
<p><img title="在道格拉斯-亚当斯的《银河系漫游指南》中，一个外星文明制造了一台巨型计算机，用于揭开宇宙的一系列谜团。不幸的是，这台计算机给出的答案是“42”" src="http://www.ha97.com/wp-content/uploads/image/2012/05/0332067d3.jpg" alt="在道格拉斯-亚当斯的《银河系漫游指南》中，一个外星文明制造了一台巨型计算机，用于揭开宇宙的一系列谜团。不幸的是，这台计算机给出的答案是“42”" /><br />
在道格拉斯-亚当斯的《银河系漫游指南》中，一个外星文明制造了一台巨型计算机，用于揭开宇宙的一系列谜团。不幸的是，这台计算机给出的答案是“42”</p>
<p><img title="平方公里阵列由数千个碟形天线构成，用于收集射电信号，天线覆盖面积超过1平方公里，整个阵列的宽度相当于美国大陆" src="http://www.ha97.com/wp-content/uploads/image/2012/05/033208QbG.jpg" alt="平方公里阵列由数千个碟形天线构成，用于收集射电信号，天线覆盖面积超过1平方公里，整个阵列的宽度相当于美国大陆" /><br />
平方公里阵列由数千个碟形天线构成，用于收集射电信号，天线覆盖面积超过1平方公里，整个阵列的宽度相当于美国大陆</p>
<p><img title="IBM正在研制3D芯片，用以达到平方公里阵列需要的运算能力" src="http://www.ha97.com/wp-content/uploads/image/2012/05/033209Q4k.jpg" alt="IBM正在研制3D芯片，用以达到平方公里阵列需要的运算能力" /><br />
IBM正在研制3D芯片，用以达到平方公里阵列需要的运算能力</p>
<p><img title="艺术概念图，展示了平方公里阵列的碟形天线。这个望远镜阵列用于观测宇宙中的电磁辐射，揭开宇宙的一系列谜团" src="http://www.ha97.com/wp-content/uploads/image/2012/05/033210rnF.jpg" alt="艺术概念图，展示了平方公里阵列的碟形天线。这个望远镜阵列用于观测宇宙中的电磁辐射，揭开宇宙的一系列谜团" /><br />
艺术概念图，展示了平方公里阵列的碟形天线。这个望远镜阵列用于观测宇宙中的电磁辐射，揭开宇宙的一系列谜团</p>
<p><img title="平方公里阵列的碟形天线拥有不可思议的灵敏度，能够发现距地球50光年的一颗行星上的机场雷达" src="http://www.ha97.com/wp-content/uploads/image/2012/05/033211Wbn.jpg" alt="平方公里阵列的碟形天线拥有不可思议的灵敏度，能够发现距地球50光年的一颗行星上的机场雷达" /><br />
平方公里阵列的碟形天线拥有不可思议的灵敏度，能够发现距地球50光年的一颗行星上的机场雷达</p>
<p>新浪科技讯 北京时间4月6日消息，据国外媒体报道，美国IBM公司正在设计史上最强的计算机，每天的数据吞吐数量相当于整个互联网，可用于对来自太空的无线电波进行 分析，探索宇宙起源的奥秘。这台计算机将与占地1900平方英里(约合4920平方公里)的望远镜阵列——平方公里阵列相连，负责分析望远镜获取的数据。 平方公里阵列能够观测到130亿年前的宇宙景象，当时刚发生大爆炸不久，正是宇宙的黎明时期。</p>
<p>IBM设计的最强计算机运算能力是当前速度最快的PC的数百万倍，负责处理的数据数量将达到大型强子对撞机所产生数据的100倍。IBM研发部 门的汤恩-恩波森表示：“平方公里阵列每天获取的数据相当于当前全球每天互联网流量的两倍。”据悉，平方公里阵列将于2024年竣工，能够观测到130亿 年前的宇宙景象，将用于研究星系演化、暗物质以及宇宙的起源。</p>
<p>IBM将采用3D芯片制造这台计算机，用以达到平方公里阵列需要的运算能力。平方公里阵列将安装数千个碟形天线，天线覆盖面积超过1平方公里， 整个阵列的宽度相当于美国大陆。这个望远镜阵列的灵敏度是此前任何射电望远镜的50倍，速度是当前望远镜的1万倍。平方公里阵列每平方公里每天产生的数据 估计可达到数EB(艾字节)。经过处理后，每年存储的数据估计在300到1500PB(拍字节)之间。相比之下，欧洲核子研究组织的大型强子对撞机每年产 生的数据在15 PB左右。</p>
<p>4月3日，平方公里阵列项目负责人在阿姆斯特丹举行会议，商讨这个巨型望远镜阵列的具体建造地点。据悉，这个阵列的占地面积将达到1900平方 英里，预计于2016年动工建造。平方公里阵列项目临时总负责人迈克尔-范-哈勒姆说：“这个望远镜阵列将对我们了解地球在宇宙中的位置产生深远影响，同 时有助于加深我们对宇宙的历史和未来的了解。我们相信未来将上演一系列重大发现。”</p>
<p><img title="平方公里阵列的建造地点仍是一个未知数" src="http://www.ha97.com/wp-content/uploads/image/2012/05/033212LWq.jpg" alt="平方公里阵列的建造地点仍是一个未知数" /><br />
平方公里阵列的建造地点仍是一个未知数</p>
<p><img title="中频孔径阵列，可一次对多个太空区域进行观测" src="http://www.ha97.com/wp-content/uploads/image/2012/05/033213Ode.jpg" alt="中频孔径阵列，可一次对多个太空区域进行观测" /><br />
中频孔径阵列，可一次对多个太空区域进行观测</p>
<p><img title="艺术概念图，展示了平方公里阵列的低频孔径阵列" src="http://www.ha97.com/wp-content/uploads/image/2012/05/033214bYg.jpg" alt="艺术概念图，展示了平方公里阵列的低频孔径阵列" /><br />
艺术概念图，展示了平方公里阵列的低频孔径阵列</p>
<p><img title="一幅图解，展示了平方公里阵列的碟形天线和孔径阵列" src="http://www.ha97.com/wp-content/uploads/image/2012/05/033215eVl.jpg" alt="一幅图解，展示了平方公里阵列的碟形天线和孔径阵列" /><br />
一幅图解，展示了平方公里阵列的碟形天线和孔径阵列</p>
<p><img title="借助于平方公里阵列，科学家将加深对黑洞以及恒星如何诞生的了解，同时也可确定太阳系外是否有生命存在" src="http://www.ha97.com/wp-content/uploads/image/2012/05/033217B3r.jpg" alt="借助于平方公里阵列，科学家将加深对黑洞以及恒星如何诞生的了解，同时也可确定太阳系外是否有生命存在" /><br />
借助于平方公里阵列，科学家将加深对黑洞以及恒星如何诞生的了解，同时也可确定太阳系外是否有生命存在</p>
<p><img title="平方公里阵列的原型KAT-7，目前正在南非卡鲁沙漠接受测试" src="http://www.ha97.com/wp-content/uploads/image/2012/05/033217G0R.jpg" alt="平方公里阵列的原型KAT-7，目前正在南非卡鲁沙漠接受测试" /><br />
平方公里阵列的原型KAT-7，目前正在南非卡鲁沙漠接受测试</p>
<p>平方公里阵列将由数千个碟形天线构成，天线总面积达到1平方公里，能够产生数量惊人的数据，彻底改变我们对宇宙的认知。英国科学探索中心协会的 伊恩-格里芬博士表示：“平方公里阵列项目将为天文学家提供一个全新的工具，让我们对宇宙的认知发生革命性变化。这个阵列能够对面积巨大的太空区域进行观 测，呈现难以置信的星系细节，有助于科学家研究神秘而令人兴奋的黑洞等天体，进而验证相对论，同时也允许天文学家进一步了解宇宙的早期历史。”</p>
<p>科学界认为他们可以借助平方公里阵列确定太阳系外是否有生命存在。搜寻系外生命需要使用具有突破性的技术。平方公里阵列的碟形天线直径15米， 能够探测天体放射出的电磁辐射。它们拥有不可思议的灵敏度，能够发现距地球50光年的一颗行星上的机场雷达。碟形天线将在低频和中频孔径阵列的协助下进行 观测，这些阵列能够让平方公里阵列拥有广阔的视野，一次可对多个太空区域进行观测。</p>
<p>平方公里阵列使用的光缆长度惊人，可绕地球两周。目前，这一望远镜阵列的建造地点仍是一个未知数，非洲南部和澳大利亚均在考虑范围之内。这两个 地区都拥有适于望远镜进行观测的环境。望远镜必须在没有电子干扰——例如手机产生的干扰——的情况下才能获得最清晰的“视线”。平方公里阵列预计于 2016年动工，碟形天线将在8年后投入使用。这一阵列的原型KAT-7由7个直径16米的碟形天线构成，目前正在南非卡鲁沙漠接受测试。</p>
<p>转自：http://www.oschina.net/news/27590/ibm-power-computer</p>
]]></content:encoded>
			<wfw:commentRss>http://www.ha97.com/4590.html/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>MongoDB架构图解</title>
		<link>http://www.ha97.com/4580.html</link>
		<comments>http://www.ha97.com/4580.html#comments</comments>
		<pubDate>Sat, 12 May 2012 10:25:11 +0000</pubDate>
		<dc:creator>谋万世全局者</dc:creator>
				<category><![CDATA[DataBase]]></category>
		<category><![CDATA[Linux]]></category>
		<category><![CDATA[UNIX]]></category>
		<category><![CDATA[外文翻译]]></category>
		<category><![CDATA[MongoDB]]></category>
		<category><![CDATA[图解]]></category>
		<category><![CDATA[架构]]></category>

		<guid isPermaLink="false">http://www.ha97.com/?p=4580</guid>
		<description><![CDATA[本文图片来自Ricky Ho的博文MongoDB构架（MongoDB Architecture），这是个一听就感觉很宽泛的话题，但是作者在文章中确实对MongoDB由内至外的架构进行了剖析。本文截取了其文章中的几张重点架构示意图片进行简单描述。希望对大家有用。 MongoDB数据文件内部结构 MongoDB在数据存储上按命名空间来划分，一个collection是一个命名空间，一个索引也是一个命名空间 同一个命名空间的数据被分成很多个Extent，Extent之间使用双向链表连接 在每一个Extent中，保存了具体每一行的数据，这些数据也是通过双向链接连接的 每一行数据存储空间不仅包括数据占用空间，还可能包含一部分附加空间，这使得在数据update变大后可以不移动位置 索引以BTree结构实现 相关阅读：《MongoDB数据文件内部结构》 在MongoDB中实现事务 众所周知，MongoDB只支持对单行记录的原子性修改，并不支持对多行数据的原子操作。但是通过上图中的变态操作，实际你也可以自己实现事务。其步骤如图所未： 第1步：先记录一条事务记录，将要修改的多行记录的修改值写到里面，并设置其状态为init（如果这时候操作中断，那么在重新启动时，会判断到他处于init状态，从而将其保存的多行修改操作应用到具体的行上） 第2步：然后更新具体要修改的行，将刚才写的事务记录的标识写到它的tran字段中 第3步：将事务记录的状态从init变成pending（如果在这时候操作中断，那么在重新启动时，会判断到它的状态是pending的，这时候查看其所有对应的多条要修改的记录，如果其tran有值，那么就进行第4步，如果没值，说明第4步已经执行过了，直接将其状态从pending变成commited了就行） 第4步：将需要修改的多条记录的相应值修改了，并且unset掉之前的tran字段 第5步：将事务记录那一条的状态从pending变成commited，事务完成 其实上面的步骤并不罕见，在支持事务的DBMS中，其事务原子性提交的保证大多都与上面类似。其实事务记录的tran那条记录，就类似于这些DBMS中的redolog一样。 MongoDB数据同步 上图是MongoDB采用Replica Sets模式的同步流程 红色箭头表示写操作写到Primary上，然后异步同步到多个Secondary上 蓝色箭头表示读操作可以从Primary或Secondary任意一个上读 各个Primary与Secondary之间一直保持心跳同步检测，用于判断Replica Sets的状态 分片机制 MongoDB的分片是指定一个分片key来进行，数据按范围分成不同的chunk，每个chunk的大小有限制 有多个分片节点保存这些chunk，每个节点保存一部分的chunk 每一个分片节点都是一个Replica Sets，这样保证数据的安全性 当一个chunk超过其限制的最大体积时，会分裂成两个小的chunk 当chunk在分片节点中分布不均衡时，会引发chunk迁移操作 服务器角色 上面讲了分片的标准，下面是具体在分片时的几种节点角色 客户端访问路由节点mongos来进行数据读写 config服务器保存了两个映射关系，一个是key值的区间对应哪一个chunk的映射关系，另一个是chunk存在哪一个分片节点的映射关系 路由节点通过config服务器获取数据信息，通过这些信息，找到真正存放数据的分片节点进行对应操作 路由节点还会在写操作时判断当前chunk是否超出限定大小，如果超出，就分列成两个chunk 对于按分片key进行的查询和update操作来说，路由节点会查到具体的chunk然后再进行相关的工作 对于不按分片key进行的查询和update操作来说，mongos会对所有下属节点发送请求然后再对返回结果进行合并 更多详细内容请看原文：MongoDB Architecture 翻译：http://blog.nosqlfan.com/html/3887.html]]></description>
			<content:encoded><![CDATA[<p>本文图片来自Ricky Ho的博文MongoDB构架（MongoDB Architecture），这是个一听就感觉很宽泛的话题，但是作者在文章中确实对MongoDB由内至外的架构进行了剖析。本文截取了其文章中的几张重点架构示意图片进行简单描述。希望对大家有用。</p>
<h3>MongoDB数据文件内部结构</h3>
<p><a href="http://www.ha97.com/wp-content/uploads/image/2012/05/102513XeW.png" target="_blank"><img src="http://www.ha97.com/wp-content/uploads/image/2012/05/102513XeW.png" alt="" width="610" /></a><br />
<span id="more-4580"></span></p>
<ol>
<li>MongoDB在数据存储上按命名空间来划分，一个collection是一个命名空间，一个索引也是一个命名空间</li>
<li>同一个命名空间的数据被分成很多个Extent，Extent之间使用双向链表连接</li>
<li>在每一个Extent中，保存了具体每一行的数据，这些数据也是通过双向链接连接的</li>
<li>每一行数据存储空间不仅包括数据占用空间，还可能包含一部分附加空间，这使得在数据update变大后可以不移动位置</li>
<li>索引以BTree结构实现</li>
</ol>
<p>相关阅读：《<a rel="nofollow" target="_blank" href="http://blog.nosqlfan.com/html/3515.html" target="_blank">MongoDB数据文件内部结构</a>》</p>
<h3>在MongoDB中实现事务</h3>
<p><a href="http://www.ha97.com/wp-content/uploads/image/2012/05/102516V22.png" target="_blank"><img src="http://www.ha97.com/wp-content/uploads/image/2012/05/102516V22.png" alt="" width="610" /></a></p>
<p>众所周知，MongoDB只支持对单行记录的原子性修改，并不支持对多行数据的原子操作。但是通过上图中的变态操作，实际你也可以自己实现事务。其步骤如图所未：</p>
<ul>
<li>第1步：先记录一条事务记录，将要修改的多行记录的修改值写到里面，并设置其状态为init（如果这时候操作中断，那么在重新启动时，会判断到他处于init状态，从而将其保存的多行修改操作应用到具体的行上）</li>
<li>第2步：然后更新具体要修改的行，将刚才写的事务记录的标识写到它的tran字段中</li>
<li>第3步：将事务记录的状态从init变成pending（如果在这时候操作中断，那么在重新启动时，会判断到它的状态是pending的，这时候查看其所有对应的多条要修改的记录，如果其tran有值，那么就进行第4步，如果没值，说明第4步已经执行过了，直接将其状态从pending变成commited了就行）</li>
<li>第4步：将需要修改的多条记录的相应值修改了，并且unset掉之前的tran字段</li>
<li>第5步：将事务记录那一条的状态从pending变成commited，事务完成</li>
</ul>
<p>其实上面的步骤并不罕见，在支持事务的DBMS中，其事务原子性提交的保证大多都与上面类似。其实事务记录的tran那条记录，就类似于这些DBMS中的redolog一样。</p>
<h3>MongoDB数据同步</h3>
<p><a href="http://www.ha97.com/wp-content/uploads/image/2012/05/102518HNR.png" target="_blank"><img src="http://www.ha97.com/wp-content/uploads/image/2012/05/102518HNR.png" alt="" width="610" /></a></p>
<p>上图是MongoDB采用Replica Sets模式的同步流程</p>
<ul>
<li>红色箭头表示写操作写到Primary上，然后异步同步到多个Secondary上</li>
<li>蓝色箭头表示读操作可以从Primary或Secondary任意一个上读</li>
<li>各个Primary与Secondary之间一直保持心跳同步检测，用于判断Replica Sets的状态</li>
</ul>
<h3>分片机制</h3>
<p><a href="http://www.ha97.com/wp-content/uploads/image/2012/05/102520Gfb.png" target="_blank"><img src="http://www.ha97.com/wp-content/uploads/image/2012/05/102520Gfb.png" alt="" width="610" /></a></p>
<ul>
<li>MongoDB的分片是指定一个分片key来进行，数据按范围分成不同的chunk，每个chunk的大小有限制</li>
<li>有多个分片节点保存这些chunk，每个节点保存一部分的chunk</li>
<li>每一个分片节点都是一个Replica Sets，这样保证数据的安全性</li>
<li>当一个chunk超过其限制的最大体积时，会分裂成两个小的chunk</li>
<li>当chunk在分片节点中分布不均衡时，会引发chunk迁移操作</li>
</ul>
<h3>服务器角色</h3>
<p><a href="http://www.ha97.com/wp-content/uploads/image/2012/05/102523CnC.png" target="_blank"><img src="http://www.ha97.com/wp-content/uploads/image/2012/05/102523CnC.png" alt="" width="610" /></a></p>
<p>上面讲了分片的标准，下面是具体在分片时的几种节点角色</p>
<ul>
<li>客户端访问路由节点mongos来进行数据读写</li>
<li>config服务器保存了两个映射关系，一个是key值的区间对应哪一个chunk的映射关系，另一个是chunk存在哪一个分片节点的映射关系</li>
<li>路由节点通过config服务器获取数据信息，通过这些信息，找到真正存放数据的分片节点进行对应操作</li>
<li>路由节点还会在写操作时判断当前chunk是否超出限定大小，如果超出，就分列成两个chunk</li>
<li>对于按分片key进行的查询和update操作来说，路由节点会查到具体的chunk然后再进行相关的工作</li>
<li>对于不按分片key进行的查询和update操作来说，mongos会对所有下属节点发送请求然后再对返回结果进行合并</li>
</ul>
<p>更多详细内容请看原文：<a rel="nofollow" target="_blank" href="http://horicky.blogspot.jp/2012/04/mongodb-architecture.html" target="_blank">MongoDB Architecture</a></p>
<p>翻译：http://blog.nosqlfan.com/html/3887.html</p>
]]></content:encoded>
			<wfw:commentRss>http://www.ha97.com/4580.html/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Memcached原理深度分析详解</title>
		<link>http://www.ha97.com/4575.html</link>
		<comments>http://www.ha97.com/4575.html#comments</comments>
		<pubDate>Sat, 12 May 2012 10:18:30 +0000</pubDate>
		<dc:creator>谋万世全局者</dc:creator>
				<category><![CDATA[DataBase]]></category>
		<category><![CDATA[Linux]]></category>
		<category><![CDATA[UNIX]]></category>
		<category><![CDATA[Memcached]]></category>
		<category><![CDATA[原理]]></category>
		<category><![CDATA[深度分析]]></category>
		<category><![CDATA[详解]]></category>

		<guid isPermaLink="false">http://www.ha97.com/?p=4575</guid>
		<description><![CDATA[Memcached是danga.com（运营LiveJournal的技术团队）开发的一套分布式内存对象缓存系统，用于在动态系统中减少数据库负载，提升性能。关于这个东西，相信很多人都用过，本文意在通过对memcached的实现及代码分析，获得对这个出色的开源软件更深入的了解，并可以根据我们的需要对其进行更进一步的优化。末了将通过对BSM_Memcache扩展的分析，加深对memcached的使用方式理解。 本文的部分内容可能需要比较好的数学基础作为辅助。 ◎Memcached是什么 在阐述这个问题之前，我们首先要清楚它“不是什么”。很多人把它当作和SharedMemory那种形式的存储载体来使用，虽然memcached使用了同样的“Key=&#62;Value”方式组织数据，但是它和共享内存、APC等本地缓存有非常大的区别。Memcached是分布式的，也就是说它不是本地的。它基于网络连接（当然它也可以使用localhost）方式完成服务，本身它是一个独立于应用的程序或守护进程（Daemon方式）。 Memcached使用libevent库实现网络连接服务，理论上可以处理无限多的连接，但是它和Apache不同，它更多的时候是面向稳定的持续连接的，所以它实际的并发能力是有限制的。在保守情况下memcached的最大同时连接数为200，这和Linux线程能力有关系，这个数值是可以调整的。关于libevent可以参考相关文档。 Memcached内存使用方式也和APC不同。APC是基于共享内存和MMAP的，memcachd有自己的内存分配算法和管理方式，它和共享内存没有关系，也没有共享内存的限制，通常情况下，每个memcached进程可以管理2GB的内存空间，如果需要更多的空间，可以增加进程数。 ◎Memcached适合什么场合 在很多时候，memcached都被滥用了，这当然少不了对它的抱怨。我经常在论坛上看见有人发贴，类似于“如何提高效率”，回复是“用memcached”，至于怎么用，用在哪里，用来干什么一句没有。memcached不是万能的，它也不是适用在所有场合。 Memcached是“分布式”的内存对象缓存系统，那么就是说，那些不需要“分布”的，不需要共享的，或者干脆规模小到只有一台服务器的应用，memcached不会带来任何好处，相反还会拖慢系统效率，因为网络连接同样需要资源，即使是UNIX本地连接也一样。 在我之前的测试数据中显示，memcached本地读写速度要比直接PHP内存数组慢几十倍，而APC、共享内存方式都和直接数组差不多。可见，如果只是本地级缓存，使用memcached是非常不划算的。 Memcached在很多时候都是作为数据库前端cache使用的。因为它比数据库少了很多SQL解析、磁盘操作等开销，而且它是使用内存来管理数据的，所以它可以提供比直接读取数据库更好的性能，在大型系统中，访问同样的数据是很频繁的，memcached可以大大降低数据库压力，使系统执行效率提升。另外，memcached也经常作为服务器之间数据共享的存储媒介，例如在SSO系统中保存系统单点登陆状态的数据就可以保存在memcached中，被多个应用共享。 需要注意的是，memcached使用内存管理数据，所以它是易失的，当服务器重启，或者memcached进程中止，数据便会丢失，所以memcached不能用来持久保存数据。很多人的错误理解，memcached的性能非常好，好到了内存和硬盘的对比程度，其实memcached使用内存并不会得到成百上千的读写速度提高，它的实际瓶颈在于网络连接，它和使用磁盘的数据库系统相比，好处在于它本身非常“轻”，因为没有过多的开销和直接的读写方式，它可以轻松应付非常大的数据交换量，所以经常会出现两条千兆网络带宽都满负荷了，memcached进程本身并不占用多少CPU资源的情况。 ◎Memcached的工作方式 以下的部分中，读者最好能准备一份memcached的源代码。 Memcached是传统的网络服务程序，如果启动的时候使用了-d参数，它会以守护进程的方式执行。创建守护进程由daemon.c完成，这个程序只有一个daemon函数，这个函数很简单（如无特殊说明，代码以1.2.1为准）： CODE: #include &#60;fcntl.h&#62; #include &#60;stdlib.h&#62; #include &#60;unistd.h&#62;&#160; int daemon(nochdir, noclose) int nochdir, noclose; { int fd; switch (fork()) { case -1: return (-1); case 0: break; default: _exit(0); } if (setsid() == -1) return (-1); if (!nochdir) (void)chdir(”/”); if (!noclose [...]]]></description>
			<content:encoded><![CDATA[<p>Memcached是danga.com（运营LiveJournal的技术团队）开发的一套分布式内存对象缓存系统，用于在动态系统中减少数据库负载，提升性能。关于这个东西，相信很多人都用过，本文意在通过对memcached的实现及代码分析，获得对这个出色的开源软件更深入的了解，并可以根据我们的需要对其进行更进一步的优化。末了将通过对BSM_Memcache扩展的分析，加深对memcached的使用方式理解。</p>
<p>本文的部分内容可能需要比较好的数学基础作为辅助。</p>
<p><strong>◎Memcached是什么</strong></p>
<p>在阐述这个问题之前，我们首先要清楚它“不是什么”。很多人把它当作和SharedMemory那种形式的存储载体来使用，虽然memcached使用了同样的“Key=&gt;Value”方式组织数据，但是它和共享内存、APC等本地缓存有非常大的区别。Memcached是分布式的，也就是说它不是本地的。它基于网络连接（当然它也可以使用localhost）方式完成服务，本身它是一个独立于应用的程序或守护进程（Daemon方式）。</p>
<p>Memcached使用libevent库实现网络连接服务，理论上可以处理无限多的连接，但是它和Apache不同，它更多的时候是面向稳定的持续连接的，所以它实际的并发能力是有限制的。在保守情况下memcached的最大同时连接数为200，这和Linux线程能力有关系，这个数值是可以调整的。关于libevent可以参考相关文档。 Memcached内存使用方式也和APC不同。APC是基于共享内存和MMAP的，memcachd有自己的内存分配算法和管理方式，它和共享内存没有关系，也没有共享内存的限制，通常情况下，每个memcached进程可以管理2GB的内存空间，如果需要更多的空间，可以增加进程数。</p>
<p><strong>◎Memcached适合什么场合</strong></p>
<p>在很多时候，memcached都被滥用了，这当然少不了对它的抱怨。我经常在论坛上看见有人发贴，类似于“如何提高效率”，回复是“用memcached”，至于怎么用，用在哪里，用来干什么一句没有。memcached不是万能的，它也不是适用在所有场合。</p>
<p><span id="more-4575"></span></p>
<p>Memcached是“分布式”的内存对象缓存系统，那么就是说，那些不需要“分布”的，不需要共享的，或者干脆规模小到只有一台服务器的应用，memcached不会带来任何好处，相反还会拖慢系统效率，因为网络连接同样需要资源，即使是UNIX本地连接也一样。 在我之前的测试数据中显示，memcached本地读写速度要比直接PHP内存数组慢几十倍，而APC、共享内存方式都和直接数组差不多。可见，如果只是本地级缓存，使用memcached是非常不划算的。</p>
<p>Memcached在很多时候都是作为数据库前端cache使用的。因为它比数据库少了很多SQL解析、磁盘操作等开销，而且它是使用内存来管理数据的，所以它可以提供比直接读取数据库更好的性能，在大型系统中，访问同样的数据是很频繁的，memcached可以大大降低数据库压力，使系统执行效率提升。另外，memcached也经常作为服务器之间数据共享的存储媒介，例如在SSO系统中保存系统单点登陆状态的数据就可以保存在memcached中，被多个应用共享。</p>
<p>需要注意的是，memcached使用内存管理数据，所以它是易失的，当服务器重启，或者memcached进程中止，数据便会丢失，所以memcached不能用来持久保存数据。很多人的错误理解，memcached的性能非常好，好到了内存和硬盘的对比程度，其实memcached使用内存并不会得到成百上千的读写速度提高，它的实际瓶颈在于网络连接，它和使用磁盘的数据库系统相比，好处在于它本身非常“轻”，因为没有过多的开销和直接的读写方式，它可以轻松应付非常大的数据交换量，所以经常会出现两条千兆网络带宽都满负荷了，memcached进程本身并不占用多少CPU资源的情况。</p>
<p><strong>◎Memcached的工作方式</strong></p>
<p>以下的部分中，读者最好能准备一份memcached的源代码。</p>
<p>Memcached是传统的网络服务程序，如果启动的时候使用了-d参数，它会以守护进程的方式执行。创建守护进程由daemon.c完成，这个程序只有一个daemon函数，这个函数很简单（如无特殊说明，代码以1.2.1为准）：</p>
<div>
<div>CODE:</div>
<div></div>
</div>
<div>#include &lt;fcntl.h&gt;<br />
#include &lt;stdlib.h&gt;<br />
#include &lt;unistd.h&gt;&nbsp;</p>
<p>int<br />
daemon(nochdir, noclose)<br />
int nochdir, noclose;<br />
{<br />
int fd;</p>
<p>switch (fork()) {<br />
case -1:<br />
return (-1);<br />
case 0:<br />
break;<br />
default:<br />
_exit(0);<br />
}</p>
<p>if (setsid() == -1)<br />
return (-1);</p>
<p>if (!nochdir)<br />
(void)chdir(”/”);</p>
<p>if (!noclose &amp;&amp; (fd = open(”/dev/null”, O_RDWR, 0)) != -1) {<br />
(void)dup2(fd, STDIN_FILENO);<br />
(void)dup2(fd, STDOUT_FILENO);<br />
(void)dup2(fd, STDERR_FILENO);<br />
if (fd &gt; STDERR_FILENO)<br />
(void)close(fd);<br />
}<br />
return (0);<br />
}</p>
</div>
<p>这个函数 fork 了整个进程之后，父进程就退出，接着重新定位 STDIN 、 STDOUT 、 STDERR 到空设备， daemon 就建立成功了。</p>
<p>Memcached 本身的启动过程，在 memcached.c 的 main 函数中顺序如下：</p>
<p>1 、调用 settings_init() 设定初始化参数<br />
2 、从启动命令中读取参数来设置 setting 值<br />
3 、设定 LIMIT 参数<br />
4 、开始网络 socket 监听（如果非 socketpath 存在）（ 1.2 之后支持 UDP 方式）<br />
5 、检查用户身份（ Memcached 不允许 root 身份启动）<br />
6 、如果有 socketpath 存在，开启 UNIX 本地连接（Sock 管道）<br />
7 、如果以 -d 方式启动，创建守护进程（如上调用 daemon 函数）<br />
8 、初始化 item 、 event 、状态信息、 hash 、连接、 slab<br />
9 、如设置中 managed 生效，创建 bucket 数组<br />
10 、检查是否需要锁定内存页<br />
11 、初始化信号、连接、删除队列<br />
12 、如果 daemon 方式，处理进程 ID<br />
13 、event 开始，启动过程结束， main 函数进入循环。</p>
<p>在 daemon 方式中，因为 stderr 已经被定向到黑洞，所以不会反馈执行中的可见错误信息。</p>
<p>memcached.c 的主循环函数是 drive_machine ，传入参数是指向当前的连接的结构指针，根据 state 成员的状态来决定动作。</p>
<p>Memcached 使用一套自定义的协议完成数据交换，它的 protocol 文档可以参考： http://code.sixapart.com/svn/memcached/trunk/server/doc/protocol.txt</p>
<p>在API中，换行符号统一为\r\n</p>
<p><strong>◎Memcached的内存管理方式</strong></p>
<p>Memcached有一个很有特色的内存管理方式，为了提高效率，它使用预申请和分组的方式管理内存空间，而并不是每次需要写入数据的时候去malloc，删除数据的时候free一个指针。Memcached使用slab-&gt;chunk的组织方式管理内存。</p>
<p>1.1和1.2的slabs.c中的slab空间划分算法有一些不同，后面会分别介绍。</p>
<p>Slab可以理解为一个内存块，一个slab是memcached一次申请内存的最小单位，在memcached中，一个slab的大小默认为1048576字节（1MB），所以memcached都是整MB的使用内存。每一个slab被划分为若干个chunk，每个chunk里保存一个item，每个item同时包含了item结构体、key和value（注意在memcached中的value是只有字符串的）。slab按照自己的id分别组成链表，这些链表又按id挂在一个slabclass数组上，整个结构看起来有点像二维数组。slabclass的长度在1.1中是21，在1.2中是200。</p>
<p>slab有一个初始chunk大小，1.1中是1字节，1.2中是80字节，1.2中有一个factor值，默认为1.25</p>
<p>在1.1中，chunk大小表示为初始大小*2^n，n为classid，即：id为0的slab，每chunk大小1字节，id为1的slab，每chunk大小2字节，id为2的slab，每chunk大小4字节……id为20的slab，每chunk大小为1MB，就是说id为20的slab里只有一个chunk：</p>
<div>
<div>CODE:</div>
<div></div>
</div>
<div>void slabs_init(size_t limit) {<br />
int i;<br />
int size=1;&nbsp;</p>
<p>mem_limit = limit;<br />
for(i=0; i&lt;=POWER_LARGEST; i++, size*=2) {<br />
slabclass[i].size = size;<br />
slabclass[i].perslab = POWER_BLOCK / size;<br />
slabclass[i].slots = 0;<br />
slabclass[i].sl_curr = slabclass[i].sl_total = slabclass[i].slabs = 0;<br />
slabclass[i].end_page_ptr = 0;<br />
slabclass[i].end_page_free = 0;<br />
slabclass[i].slab_list = 0;<br />
slabclass[i].list_size = 0;<br />
slabclass[i].killing = 0;<br />
}</p>
<p>/* for the test suite:  faking of how much we’ve already malloc’d */<br />
{<br />
char *t_initial_malloc = getenv(”T_MEMD_INITIAL_MALLOC”);<br />
if (t_initial_malloc) {<br />
mem_malloced = atol(getenv(”T_MEMD_INITIAL_MALLOC”));<br />
}<br />
}</p>
<p>/* pre-allocate slabs by default, unless the environment variable<br />
for testing is set to something non-zero */<br />
{<br />
char *pre_alloc = getenv(”T_MEMD_SLABS_ALLOC”);<br />
if (!pre_alloc || atoi(pre_alloc)) {<br />
slabs_preallocate(limit / POWER_BLOCK);<br />
}<br />
}<br />
}</p>
</div>
<p>在1.2中，chunk大小表示为初始大小*f^n，f为factor，在memcached.c中定义，n为classid，同时，201个头不是全部都要初始化的，因为factor可变，初始化只循环到计算出的大小达到slab大小的一半为止，而且它是从id1开始的，即：id为1的slab，每chunk大小80字节，id为2的slab，每chunk大小80*f，id为3的slab，每chunk大小80*f^2，初始化大小有一个修正值CHUNK_ALIGN_BYTES，用来保证n-byte排列 （保证结果是CHUNK_ALIGN_BYTES的整倍数）。这样，在标准情况下，memcached1.2会初始化到id40，这个slab中每个chunk大小为504692，每个slab中有两个chunk。最后，slab_init函数会在最后补足一个id41，它是整块的，也就是这个slab中只有一个1MB大的chunk：</p>
<div>
<div>CODE:</div>
<div></div>
</div>
<div>void slabs_init(size_t limit, double factor) {<br />
int i = POWER_SMALLEST – 1;<br />
unsigned int size = sizeof(item) + settings.chunk_size;&nbsp;</p>
<p>/* Factor of 2.0 means use the default memcached behavior */<br />
if (factor == 2.0 &amp;&amp; size &lt; 128)<br />
size = 128;</p>
<p>mem_limit = limit;<br />
memset(slabclass, 0, sizeof(slabclass));</p>
<p>while (++i &lt; POWER_LARGEST &amp;&amp; size &lt;= POWER_BLOCK / 2) {<br />
/* Make sure items are always n-byte aligned */<br />
if (size % CHUNK_ALIGN_BYTES)<br />
size += CHUNK_ALIGN_BYTES – (size % CHUNK_ALIGN_BYTES);</p>
<p>slabclass[i].size = size;<br />
slabclass[i].perslab = POWER_BLOCK / slabclass[i].size;<br />
size *= factor;<br />
if (settings.verbose &gt; 1) {<br />
fprintf(stderr, “slab class %3d: chunk size %6d perslab %5d\n”,<br />
i, slabclass[i].size, slabclass[i].perslab);<br />
}<br />
}</p>
<p>power_largest = i;<br />
slabclass[power_largest].size = POWER_BLOCK;<br />
slabclass[power_largest].perslab = 1;</p>
<p>/* for the test suite:  faking of how much we’ve already malloc’d */<br />
{<br />
char *t_initial_malloc = getenv(”T_MEMD_INITIAL_MALLOC”);<br />
if (t_initial_malloc) {<br />
mem_malloced = atol(getenv(”T_MEMD_INITIAL_MALLOC”));<br />
}</p>
<p>}</p>
<p>#ifndef DONT_PREALLOC_SLABS<br />
{<br />
char *pre_alloc = getenv(”T_MEMD_SLABS_ALLOC”);<br />
if (!pre_alloc || atoi(pre_alloc)) {<br />
slabs_preallocate(limit / POWER_BLOCK);<br />
}<br />
}<br />
#endif<br />
}</p>
</div>
<p>由上可以看出，memcached的内存分配是有冗余的，当一个slab不能被它所拥有的chunk大小整除时，slab尾部剩余的空间就被丢弃了，如id40中，两个chunk占用了1009384字节，这个slab一共有1MB，那么就有39192字节被浪费了。</p>
<p>Memcached使用这种方式来分配内存，是为了可以快速的通过item长度定位出slab的classid，有一点类似hash，因为item的长度是可以计算的，比如一个item的长度是300字节，在1.2中就可以得到它应该保存在id7的slab中，因为按照上面的计算方法，id6的chunk大小是252字节，id7的chunk大小是316字节，id8的chunk大小是396字节，表示所有252到316字节的item都应该保存在id7中。同理，在1.1中，也可以计算得到它出于256和512之间，应该放在chunk_size为512的id9中(32位系统）。</p>
<p>Memcached初始化的时候，会初始化slab（前面可以看到，在main函数中调用了slabs_init()）。它会在slabs_init()中检查一个常量DONT_PREALLOC_SLABS，如果这个没有被定义，说明使用预分配内存方式初始化slab，这样在所有已经定义过的slabclass中，每一个id创建一个slab。这样就表示，1.2在默认的环境中启动进程后要分配41MB的slab空间，在这个过程里，memcached的第二个内存冗余发生了，因为有可能一个id根本没有被使用过，但是它也默认申请了一个slab，每个slab会用掉1MB内存</p>
<p>当一个slab用光后，又有新的item要插入这个id，那么它就会重新申请新的slab，申请新的slab时，对应id的slab链表就要增长，这个链表是成倍增长的，在函数grow_slab_list函数中，这个链的长度从1变成2，从2变成4，从4变成8……：</p>
<div>
<div>CODE:</div>
<div></div>
</div>
<div>static int grow_slab_list (unsigned int id) {<br />
slabclass_t *p = &amp;slabclass[id];<br />
if (p-&gt;slabs == p-&gt;list_size) {<br />
size_t new_size =  p-&gt;list_size ? p-&gt;list_size * 2 : 16;<br />
void *new_list = realloc(p-&gt;slab_list, new_size*sizeof(void*));<br />
if (new_list == 0) return 0;<br />
p-&gt;list_size = new_size;<br />
p-&gt;slab_list = new_list;<br />
}<br />
return 1;<br />
}</div>
<p>在定位item时，都是使用slabs_clsid函数，传入参数为item大小，返回值为classid，由这个过程可以看出，memcached的第三个内存冗余发生在保存item的过程中，item总是小于或等于chunk大小的，当item小于chunk大小时，就又发生了空间浪费。</p>
<p><strong>◎Memcached的NewHash算法</strong></p>
<p>Memcached的item保存基于一个大的hash表，它的实际地址就是slab中的chunk偏移，但是它的定位是依靠对key做hash的结果，在primary_hashtable中找到的。在assoc.c和items.c中定义了所有的hash和item操作。</p>
<p>Memcached使用了一个叫做NewHash的算法，它的效果很好，效率也很高。1.1和1.2的NewHash有一些不同，主要的实现方式还是一样的，1.2的hash函数是经过整理优化的，适应性更好一些。</p>
<p>NewHash的原型参考：http://burtleburtle.net/bob/hash/evahash.html。数学家总是有点奇怪，呵呵～</p>
<p>为了变换方便，定义了u4和u1两种数据类型，u4就是无符号的长整形，u1就是无符号char(0-255)。</p>
<p>具体代码可以参考1.1和1.2源码包。</p>
<p>注意这里的hashtable长度，1.1和1.2也是有区别的，1.1中定义了HASHPOWER常量为20，hashtable表长为hashsize(HASHPOWER)，就是4MB（hashsize是一个宏，表示1右移n位），1.2中是变量16，即hashtable表长65536：</p>
<div>
<div>CODE:</div>
<div></div>
</div>
<div>typedef  unsigned long  int  ub4;   /* unsigned 4-byte quantities */<br />
typedef  unsigned       char ub1;   /* unsigned 1-byte quantities */&nbsp;</p>
<p>#define hashsize(n) ((ub4)1&lt;&lt;(n))<br />
#define hashmask(n) (hashsize(n)-1)</p>
</div>
<p>在assoc_init()中，会对primary_hashtable做初始化，对应的hash操作包括：assoc_find()、assoc_expand()、assoc_move_next_bucket()、assoc_insert()、assoc_delete()，对应于item的读写操作。其中assoc_find()是根据key和key长寻找对应的item地址的函数（注意在C中，很多时候都是同时直接传入字符串和字符串长度，而不是在函数内部做strlen），返回的是item结构指针，它的数据地址在slab中的某个chunk上。</p>
<p>items.c是数据项的操作程序，每一个完整的item包括几个部分，在item_make_header()中定义为：</p>
<p>key：键<br />
nkey：键长<br />
flags：用户定义的flag（其实这个flag在memcached中没有启用）<br />
nbytes：值长（包括换行符号\r\n）<br />
suffix：后缀Buffer<br />
nsuffix：后缀长</p>
<p>一个完整的item长度是键长＋值长＋后缀长＋item结构大小（32字节），item操作就是根据这个长度来计算slab的classid的。</p>
<p>hashtable中的每一个桶上挂着一个双链表，item_init()的时候已经初始化了heads、tails、sizes三个数组为0，这三个数组的大小都为常量LARGEST_ID（默认为255，这个值需要配合factor来修改），在每次item_assoc()的时候，它会首先尝试从slab中获取一块空闲的chunk，如果没有可用的chunk，会在链表中扫描50次，以得到一个被LRU踢掉的item，将它unlink，然后将需要插入的item插入链表中。</p>
<p>注意item的refcount成员。item被unlink之后只是从链表上摘掉，不是立刻就被free的，只是将它放到删除队列中（item_unlink_q()函数）。</p>
<p>item对应一些读写操作，包括remove、update、replace，当然最重要的就是alloc操作。</p>
<p>item还有一个特性就是它有过期时间，这是memcached的一个很有用的特性，很多应用都是依赖于memcached的item过期，比如session存储、操作锁等。item_flush_expired()函数就是扫描表中的item，对过期的item执行unlink操作，当然这只是一个回收动作，实际上在get的时候还要进行时间判断：</p>
<div>
<div>CODE:</div>
<div></div>
</div>
<div>/* expires items that are more recent than the oldest_live setting. */<br />
void item_flush_expired() {<br />
int i;<br />
item *iter, *next;<br />
if (! settings.oldest_live)<br />
return;<br />
for (i = 0; i &lt; LARGEST_ID; i++) {<br />
/* The LRU is sorted in decreasing time order, and an item’s timestamp<br />
* is never newer than its last access time, so we only need to walk<br />
* back until we hit an item older than the oldest_live time.<br />
* The oldest_live checking will auto-expire the remaining items.<br />
*/<br />
for (iter = heads[i]; iter != NULL; iter = next) {<br />
if (iter-&gt;time &gt;= settings.oldest_live) {<br />
next = iter-&gt;next;<br />
if ((iter-&gt;it_flags &amp; ITEM_SLABBED) == 0) {<br />
item_unlink(iter);<br />
}<br />
} else {<br />
/* We’ve hit the first old item. Continue to the next queue. */<br />
break;<br />
}<br />
}<br />
}<br />
}</div>
<p>&nbsp;</p>
<div>
<div>CODE:</div>
<div></div>
</div>
<div>/* wrapper around assoc_find which does the lazy expiration/deletion logic */<br />
item *get_item_notedeleted(char *key, size_t nkey, int *delete_locked) {<br />
item *it = assoc_find(key, nkey);<br />
if (delete_locked) *delete_locked = 0;<br />
if (it &amp;&amp; (it-&gt;it_flags &amp; ITEM_DELETED)) {<br />
/* it’s flagged as delete-locked.  let’s see if that condition<br />
is past due, and the 5-second delete_timer just hasn’t<br />
gotten to it yet… */<br />
if (! item_delete_lock_over(it)) {<br />
if (delete_locked) *delete_locked = 1;<br />
it = 0;<br />
}<br />
}<br />
if (it &amp;&amp; settings.oldest_live &amp;&amp; settings.oldest_live &lt;= current_time &amp;&amp;<br />
it-&gt;time &lt;= settings.oldest_live) {<br />
item_unlink(it);<br />
it = 0;<br />
}<br />
if (it &amp;&amp; it-&gt;exptime &amp;&amp; it-&gt;exptime &lt;= current_time) {<br />
item_unlink(it);<br />
it = 0;<br />
}<br />
return it;<br />
}</div>
<p>Memcached的内存管理方式是非常精巧和高效的，它很大程度上减少了直接alloc系统内存的次数，降低函数开销和内存碎片产生几率，虽然这种方式会造成一些冗余浪费，但是这种浪费在大型系统应用中是微不足道的。<br />
<img src="http://www.ha97.com/wp-content/uploads/image/2012/05/101830S2G.png" alt="" border="0" /></p>
<p><strong>◎Memcached的理论参数计算方式</strong></p>
<p>影响 memcached 工作的几个参数有：</p>
<p>常量REALTIME_MAXDELTA 60*60*24*30<br />
最大30天的过期时间</p>
<p>conn_init()中的freetotal（=200）<br />
最大同时连接数</p>
<p>常量KEY_MAX_LENGTH 250<br />
最大键长</p>
<p>settings.factor（=1.25）<br />
factor将影响chunk的步进大小</p>
<p>settings.maxconns（=1024）<br />
最大软连接</p>
<p>settings.chunk_size（=48）<br />
一个保守估计的key+value长度，用来生成id1中的chunk长度（1.2）。id1的chunk长度等于这个数值加上item结构体的长度（32），即默认的80字节。</p>
<p>常量POWER_SMALLEST 1<br />
最小classid（1.2）</p>
<p>常量POWER_LARGEST 200<br />
最大classid（1.2）</p>
<p>常量POWER_BLOCK 1048576<br />
默认slab大小</p>
<p>常量CHUNK_ALIGN_BYTES (sizeof(void *))<br />
保证chunk大小是这个数值的整数倍，防止越界（void *的长度在不同系统上不一样，在标准32位系统上是4）</p>
<p>常量ITEM_UPDATE_INTERVAL 60<br />
队列刷新间隔</p>
<p>常量LARGEST_ID 255<br />
最大item链表数（这个值不能比最大的classid小）</p>
<p>变量hashpower（在1.1中是常量HASHPOWER）<br />
决定hashtable的大小</p>
<p>根据上面介绍的内容及参数设定，可以计算出的一些结果：</p>
<p>1、在memcached中可以保存的item个数是没有软件上限的，之前我的100万的说法是错误的。<br />
2、假设NewHash算法碰撞均匀，查找item的循环次数是item总数除以hashtable大小（由hashpower决定），是线性的。<br />
3、Memcached限制了可以接受的最大item是1MB，大于1MB的数据不予理会。<br />
4、Memcached的空间利用率和数据特性有很大的关系，又与DONT_PREALLOC_SLABS常量有关。 在最差情况下，有198个slab会被浪费（所有item都集中在一个slab中，199个id全部分配满）。</p>
<p><strong>◎Memcached的定长优化</strong></p>
<p>根据上面几节的描述，多少对memcached有了一个比较深入的认识。在深入认识的基础上才好对它进行优化。</p>
<p>Memcached本身是为变长数据设计的，根据数据特性，可以说它是“面向大众”的设计，但是很多时候，我们的数据并不是这样的“普遍”，典型的情况中，一种是非均匀分布，即数据长度集中在几个区域内（如保存用户 Session）；另一种更极端的状态是等长数据（如定长键值，定长数据，多见于访问、在线统计或执行锁）。</p>
<p>这里主要研究一下定长数据的优化方案（1.2），集中分布的变长数据仅供参考，实现起来也很容易。</p>
<p>解决定长数据，首先需要解决的是slab的分配问题，第一个需要确认的是我们不需要那么多不同chunk长度的slab，为了最大限度地利用资源，最好chunk和item等长，所以首先要计算item长度。</p>
<p>在之前已经有了计算item长度的算法，需要注意的是，除了字符串长度外，还要加上item结构的长度32字节。</p>
<p>假设我们已经计算出需要保存200字节的等长数据。</p>
<p>接下来是要修改slab的classid和chunk长度的关系。在原始版本中，chunk长度和classid是有对应关系的，现在如果把所有的chunk都定为200个字节，那么这个关系就不存在了，我们需要重新确定这二者的关系。一种方法是，整个存储结构只使用一个固定的id，即只使用199个槽中的1个，在这种条件下，就一定要定义DONT_PREALLOC_SLABS来避免另外的预分配浪费。另一种方法是建立一个hash关系，来从item确定classid，不能使用长度来做键，可以使用key的NewHash结果等不定数据，或者直接根据key来做hash（定长数据的key也一定等长）。这里简单起见，选择第一种方法，这种方法的不足之处在于只使用一个id，在数据量非常大的情况下，slab链会很长（因为所有数据都挤在一条链上了），遍历起来的代价比较高。</p>
<p>前面介绍了三种空间冗余，设置chunk长度等于item长度，解决了第一种空间浪费问题，不预申请空间解决了第二种空间浪费问题，那么对于第一种问题（slab内剩余）如何解决呢，这就需要修改POWER_BLOCK常量，使得每一个slab大小正好等于chunk长度的整数倍，这样一个slab就可以正好划分成n个chunk。这个数值应该比较接近1MB，过大的话同样会造成冗余，过小的话会造成次数过多的alloc，根据chunk长度为200，选择1000000作为POWER_BLOCK的值，这样一个slab就是100万字节，不是1048576。三个冗余问题都解决了，空间利用率会大大提升。</p>
<p>修改 slabs_clsid 函数，让它直接返回一个定值（比如 1 ）：</p>
<div>
<div>CODE:</div>
<div></div>
</div>
<div>unsigned int slabs_clsid(size_t size) {<br />
return 1;<br />
}</div>
<p>修改slabs_init函数，去掉循环创建所有classid属性的部分，直接添加slabclass[1]：</p>
<div>
<div>CODE:</div>
<div></div>
</div>
<div>slabclass[1].size = 200;                //每chunk200字节<br />
slabclass[1].perslab = 5000;        //1000000/200</div>
<p><strong>◎Memcached客户端</strong></p>
<p>Memcached是一个服务程序，使用的时候可以根据它的协议，连接到memcached服务器上，发送命令给服务进程，就可以操作上面的数据。为了方便使用，memcached有很多个客户端程序可以使用，对应于各种语言，有各种语言的客户端。基于C语言的有libmemcache、APR_Memcache；基于Perl的有Cache::Memcached；另外还有Python、Ruby、Java、C#等语言的支持。PHP的客户端是最多的，不光有mcache和PECL memcache两个扩展，还有大把的由PHP编写的封装类，下面介绍一下在PHP中使用memcached的方法：</p>
<p>mcache扩展是基于libmemcache再封装的。libmemcache一直没有发布stable版本，目前版本是1.4.0-rc2，可以在这里找到。libmemcache有一个很不好的特性，就是会向stderr写很多错误信息，一般的，作为lib使用的时候，stderr一般都会被定向到其它地方，比如Apache的错误日志，而且libmemcache会自杀，可能会导致异常，不过它的性能还是很好的。</p>
<p>mcache扩展最后更新到1.2.0-beta10，作者大概是离职了，不光停止更新，连网站也打不开了（~_~），只能到其它地方去获取这个不负责的扩展了。解压后安装方法如常：phpize &amp; configure &amp; make &amp; make install，一定要先安装libmemcache。使用这个扩展很简单：</p>
<div>
<div>CODE:</div>
<div></div>
</div>
<div>&lt;?php<br />
$mc = memcache();    // 创建一个memcache连接对象，注意这里不是用new！<br />
$mc-&gt;add_server(‘localhost’, 11211);    // 添加一个服务进程<br />
$mc-&gt;add_server(‘localhost’, 11212);    // 添加第二个服务进程<br />
$mc-&gt;set(‘key1′, ‘Hello’);    // 写入key1 =&gt; Hello<br />
$mc-&gt;set(‘key2′, ‘World’, 10);    // 写入key2 =&gt; World，10秒过期<br />
$mc-&gt;set(‘arr1′, array(‘Hello’, ‘World’));    // 写入一个数组<br />
$key1 = $mc-&gt;get(‘key1′);    // 获取’key1′的值，赋给$key1<br />
$key2 = $mc-&gt;get(‘key2′);    // 获取’key2′的值，赋给$key2，如果超过10秒，就取不到了<br />
$arr1 = $mc-&gt;get(‘arr1′);    // 获取’arr1′数组<br />
$mc-&gt;delete(‘arr1′);    // 删除’arr1′<br />
$mc-&gt;flush_all();    // 删掉所有数据<br />
$stats = $mc-&gt;stats();    // 获取服务器信息<br />
var_dump($stats);    // 服务器信息是一个数组<br />
?&gt;</div>
<p>这个扩展的好处是可以很方便地实现分布式存储和负载均衡，因为它可以添加多个服务地址，数据在保存的时候是会根据hash结果定位到某台服务器上的，这也是libmemcache的特性。libmemcache支持集中hash方式，包括CRC32、ELF和Perl hash。</p>
<p>PECL memcache是PECL发布的扩展，目前最新版本是2.1.0，可以在pecl网站得到。memcache扩展的使用方法可以在新一些的PHP手册中找到，它和mcache很像，真的很像：</p>
<div>
<div>CODE:</div>
<div></div>
</div>
<div>&lt;?php&nbsp;</p>
<p>$memcache = new Memcache;<br />
$memcache-&gt;connect(‘localhost’, 11211) or die (“Could not connect”);</p>
<p>$version = $memcache-&gt;getVersion();<br />
echo “Server’s version: ”.$version.“n”;</p>
<p>$tmp_object = new stdClass;<br />
$tmp_object-&gt;str_attr = ‘test’;<br />
$tmp_object-&gt;int_attr = 123;</p>
<p>$memcache-&gt;set(‘key’, $tmp_object, false, 10) or die (“Failed to save data at the server”);<br />
echo “Store data in the cache (data will expire in 10 seconds)n”;</p>
<p>$get_result = $memcache-&gt;get(‘key’);<br />
echo “Data from the cache:n”;</p>
<p>var_dump($get_result);</p>
<p>?&gt;</p>
</div>
<p>这个扩展是使用php的stream直接连接memcached服务器并通过socket发送命令的。它不像libmemcache那样完善，也不支持add_server这种分布操作，但是因为它不依赖其它的外界程序，兼容性要好一些，也比较稳定。至于效率，差别不是很大。</p>
<p>另外，有很多的PHP class可以使用，比如MemcacheClient.inc.php，phpclasses.org上可以找到很多，一般都是对perl client API的再封装，使用方式很像。</p>
<p>◎BSM_Memcache</p>
<p>从C client来说，APR_Memcache是一个很成熟很稳定的client程序，支持线程锁和原子级操作，保证运行的稳定性。不过它是基于APR的（APR将在最后一节介绍），没有libmemcache的应用范围广，目前也没有很多基于它开发的程序，现有的多是一些Apache Module，因为它不能脱离APR环境运行。但是APR倒是可以脱离Apache单独安装的，在APR网站上可以下载APR和APR-util，不需要有Apache，可以直接安装，而且它是跨平台的。</p>
<p>BSM_Memcache是我在BS.Magic项目中开发的一个基于APR_Memcache的PHP扩展，说起来有点拗口，至少它把APR扯进了PHP扩展中。这个程序很简单，也没做太多的功能，只是一种形式的尝试，它支持服务器分组。</p>
<p>和mcache扩展支持多服务器分布存储不同，BSM_Memcache支持多组服务器，每一组内的服务器还是按照hash方式来分布保存数据，但是两个组中保存的数据是一样的，也就是实现了热备，它不会因为一台服务器发生单点故障导致数据无法获取，除非所有的服务器组都损坏（例如机房停电）。当然实现这个功能的代价就是性能上的牺牲，在每次添加删除数据的时候都要扫描所有的组，在get数据的时候会随机选择一组服务器开始轮询，一直到找到数据为止，正常情况下一次就可以获取得到。</p>
<p>BSM_Memcache只支持这几个函数：</p>
<div>
<div>CODE:</div>
<div></div>
</div>
<div>zend_function_entry bsm_memcache_functions[] =<br />
{<br />
PHP_FE(mc_get,          NULL)<br />
PHP_FE(mc_set,          NULL)<br />
PHP_FE(mc_del,          NULL)<br />
PHP_FE(mc_add_group,    NULL)<br />
PHP_FE(mc_add_server,   NULL)<br />
PHP_FE(mc_shutdown,     NULL)<br />
{NULL, NULL, NULL}<br />
};</div>
<p>mc_add_group函数返回一个整形（其实应该是一个object，我偷懒了~_~）作为组ID，mc_add_server的时候要提供两个参数，一个是组ID，一个是服务器地址（ADDR<img src="http://www.ha97.com/wp-content/uploads/image/2012/05/1018314pg.gif" alt="" align="absMiddle" border="0" />ORT）。</p>
<div>
<div>CODE:</div>
<div></div>
</div>
<div>/**<br />
* Add a server group<br />
*/<br />
PHP_FUNCTION(mc_add_group)<br />
{<br />
apr_int32_t group_id;<br />
apr_status_t rv;&nbsp;</p>
<p>if (0 != ZEND_NUM_ARGS())<br />
{<br />
WRONG_PARAM_COUNT;<br />
RETURN_NULL();<br />
}</p>
<p>group_id = free_group_id();<br />
if (-1 == group_id)<br />
{<br />
RETURN_FALSE;<br />
}</p>
<p>apr_memcache_t *mc;<br />
rv = apr_memcache_create(p, MAX_G_SERVER, 0, &amp;mc);</p>
<p>add_group(group_id, mc);</p>
<p>RETURN_DOUBLE(group_id);<br />
}</p>
</div>
<p>&nbsp;</p>
<div>
<div>CODE:</div>
<div></div>
</div>
<div>/**<br />
* Add a server into group<br />
*/<br />
PHP_FUNCTION(mc_add_server)<br />
{<br />
apr_status_t rv;<br />
apr_int32_t group_id;<br />
double g;<br />
char *srv_str;<br />
int srv_str_l;&nbsp;</p>
<p>if (2 != ZEND_NUM_ARGS())<br />
{<br />
WRONG_PARAM_COUNT;<br />
}</p>
<p>if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, “ds”, &amp;g, &amp;srv_str, &amp;srv_str_l) == FAILURE)<br />
{<br />
RETURN_FALSE;<br />
}</p>
<p>group_id = (apr_int32_t) g;</p>
<p>if (-1 == is_validate_group(group_id))<br />
{<br />
RETURN_FALSE;<br />
}</p>
<p>char *host, *scope;<br />
apr_port_t port;</p>
<p>rv = apr_parse_addr_port(&amp;host, &amp;scope, &amp;port, srv_str, p);<br />
if (APR_SUCCESS == rv)<br />
{<br />
// Create this server object<br />
apr_memcache_server_t *st;<br />
rv = apr_memcache_server_create(p, host, port, 0, 64, 1024, 600, &amp;st);<br />
if (APR_SUCCESS == rv)<br />
{<br />
if (NULL == mc_groups[group_id])<br />
{<br />
RETURN_FALSE;<br />
}</p>
<p>// Add server<br />
rv = apr_memcache_add_server(mc_groups[group_id], st);</p>
<p>if (APR_SUCCESS == rv)<br />
{<br />
RETURN_TRUE;<br />
}<br />
}<br />
}</p>
<p>RETURN_FALSE;<br />
}</p>
</div>
<p>在set和del数据的时候，要循环所有的组：</p>
<div>
<div>CODE:</div>
<div></div>
</div>
<div>/**<br />
* Store item into all groups<br />
*/<br />
PHP_FUNCTION(mc_set)<br />
{<br />
char *key, *value;<br />
int key_l, value_l;<br />
double ttl = 0;<br />
double set_ct = 0;&nbsp;</p>
<p>if (2 != ZEND_NUM_ARGS())<br />
{<br />
WRONG_PARAM_COUNT;<br />
}</p>
<p>if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, “ss|d”, &amp;key, &amp;key_l, &amp;value, &amp;value_l, ttl) == FAILURE)<br />
{<br />
RETURN_FALSE;<br />
}</p>
<p>// Write data into every object<br />
apr_int32_t i = 0;<br />
if (ttl &lt; 0)<br />
{<br />
ttl = 0;<br />
}</p>
<p>apr_status_t rv;</p>
<p>for (i = 0; i &lt; MAX_GROUP; i++)<br />
{<br />
if (0 == is_validate_group(i))<br />
{<br />
// Write it!<br />
rv = apr_memcache_add(mc_groups[i], key, value, value_l, (apr_uint32_t) ttl, 0);<br />
if (APR_SUCCESS == rv)<br />
{<br />
set_ct++;<br />
}<br />
}<br />
}</p>
<p>RETURN_DOUBLE(set_ct);<br />
}</p>
</div>
<p>在mc_get中，首先要随机选择一个组，然后从这个组开始轮询：</p>
<div>
<div>CODE:</div>
<div></div>
</div>
<div>/**<br />
* Fetch a item from a random group<br />
*/<br />
PHP_FUNCTION(mc_get)<br />
{<br />
char *key, *value = NULL;<br />
int key_l;<br />
apr_size_t value_l;&nbsp;</p>
<p>if (1 != ZEND_NUM_ARGS())<br />
{<br />
WRONG_PARAM_COUNT;<br />
}</p>
<p>if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, “s”, &amp;key, &amp;key_l) == FAILURE)<br />
{<br />
RETURN_MULL();<br />
}</p>
<p>// I will try …<br />
// Random read<br />
apr_int32_t curr_group_id = random_group();<br />
apr_int32_t i = 0;<br />
apr_int32_t try = 0;<br />
apr_uint32_t flag;<br />
apr_memcache_t *oper;<br />
apr_status_t rv;</p>
<p>for (i = 0; i &lt; MAX_GROUP; i++)<br />
{<br />
try = i + curr_group_id;<br />
try = try % MAX_GROUP;<br />
if (0 == is_validate_group(try))<br />
{<br />
// Get a value<br />
oper = mc_groups[try];<br />
rv = apr_memcache_getp(mc_groups[try], p, (const char *) key, &amp;value, &amp;value_l, 0);<br />
if (APR_SUCCESS == rv)<br />
{<br />
RETURN_STRING(value, 1);<br />
}<br />
}<br />
}</p>
<p>RETURN_FALSE;<br />
}</p>
</div>
<p>&nbsp;</p>
<div>
<div>CODE:</div>
<div></div>
</div>
<div>/**<br />
* Random group id<br />
* For mc_get()<br />
*/<br />
apr_int32_t random_group()<br />
{<br />
struct timeval tv;<br />
struct timezone tz;<br />
int usec;&nbsp;</p>
<p>gettimeofday(&amp;tv, &amp;tz);</p>
<p>usec = tv.tv_usec;</p>
<p>int curr = usec % count_group();</p>
<p>return (apr_int32_t) curr;<br />
}</p>
</div>
<p>BSM_Memcache的使用方式和其它的client类似：</p>
<div>
<div>CODE:</div>
<div></div>
</div>
<div>&lt;?php<br />
$g1 = mc_add_group();    // 添加第一个组<br />
$g2 = mc_add_group();    // 添加第二个组<br />
mc_add_server($g1, ‘localhost:11211′);    // 在第一个组中添加第一台服务器<br />
mc_add_server($g1, ‘localhost:11212′);    // 在第一个组中添加第二台服务器<br />
mc_add_server($g2, ‘10.0.0.16:11211′);    // 在第二个组中添加第一台服务器<br />
mc_add_server($g2, ‘10.0.0.17:11211′);    // 在第二个组中添加第二台服务器&nbsp;</p>
<p>mc_set(‘key’, ‘Hello’);    // 写入数据<br />
$key = mc_get(‘key’);    // 读出数据<br />
mc_del(‘key’);    // 删除数据<br />
mc_shutdown();    // 关闭所有组<br />
?&gt;</p>
</div>
<p>APR_Memcache的相关资料可以在这里找到，BSM_Memcache可以在本站下载。</p>
<p><strong>◎APR环境介绍</strong></p>
<p>APR的全称：Apache Portable Runtime。它是Apache软件基金会创建并维持的一套跨平台的C语言库。它从Apache httpd1.x中抽取出来并独立于httpd之外，Apache httpd2.x就是建立在APR上。APR提供了很多方便的API接口可供使用，包括如内存池、字符串操作、网络、数组、hash表等实用的功能。开发Apache2 Module要接触很多APR函数，当然APR可以独立安装独立使用，可以用来写自己的应用程序，不一定是Apache httpd的相关开发。</p>
<p><strong>◎后记</strong></p>
<p>这是我在农历丙戌年（我的本命年）的最后一篇文章，由于Memcached的内涵很多，仓促整理一定有很多遗漏和错误。感谢新浪网提供的研究机会，感谢部门同事的帮助。</p>
<p>NP博士 02-13-2007</p>
<p><em>原文发表于：http://www.54np.com/</em></p>
<p>转自：http://blog.developers.api.sina.com.cn/?p=124</p>
]]></content:encoded>
			<wfw:commentRss>http://www.ha97.com/4575.html/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Linux文件查找命令find和xargs详解</title>
		<link>http://www.ha97.com/4571.html</link>
		<comments>http://www.ha97.com/4571.html#comments</comments>
		<pubDate>Wed, 09 May 2012 10:20:53 +0000</pubDate>
		<dc:creator>谋万世全局者</dc:creator>
				<category><![CDATA[Linux]]></category>
		<category><![CDATA[Shell]]></category>
		<category><![CDATA[UNIX]]></category>
		<category><![CDATA[旧文存档]]></category>
		<category><![CDATA[find]]></category>
		<category><![CDATA[xargs]]></category>
		<category><![CDATA[命令]]></category>
		<category><![CDATA[文件查找]]></category>
		<category><![CDATA[详解]]></category>

		<guid isPermaLink="false">http://www.ha97.com/?p=4571</guid>
		<description><![CDATA[PS:find是个很强大的命令，能够匹配正则，查找对应权限，能够帮你精确的定位你的系统中的任何地方任何目录下的文件。下面这篇是关于find命令用法最详细的一篇了，记录下。还有要注意的：本文的分号；其实都是带转义字符的\\；，可能是本博的模板问题，后台显示但前台没显示出来。对于不同的系统，直接使用分号可能会有不同的意义，使用转义符\\在分号前明确说明。 总结：zhy2111314 来自：LinuxSir.Org 整理：北南南北 摘要： 本文是find命令的详细说明，可贵的是针对参数举了很多的实例，大量的例证，让初学者更为容易理解；本文是zhyfly兄贴在论坛中；我对本文进行了再次整理，为方便大家阅读。 目录 版权声明 前言：关于find命令 一、find 命令格式 1、find命令的一般形式为； 2、find命令的参数； 3、find命令选项； 4、使用exec或ok来执行shell命令； 二、find命令的例子； 1、查找当前用户主目录下的所有文件； 2、为了在当前目录中文件属主具有读、写权限，并且文件所属组的用户和其他用户具有读权限的文件； 3、为了查找系统中所有文件长度为0的普通文件，并列出它们的完整路径； 4、查找/var/logs目录中更改时间在7日以前的普通文件，并在删除之前询问它们； 5、为了查找系统中所有属于root组的文件； 6、find命令将删除当目录中访问时间在7日以来、含有数字后缀的admin.log文件 7、为了查找当前文件系统中的所有目录并排序； 8、为了查找系统中所有的rmt磁带设备； 三、xargs 四、find 命令的参数； 1、使用name选项 2、用perm选项 3、忽略某个目录 4、使用find查找文件的时候怎么避开某个文件目录 5、使用user和nouser选项 6、使用group和nogroup选项 7、按照更改时间或访问时间等查找文件 8、查找比某个文件新或旧的文件 9、使用type选项 10、使用size选项 11、使用depth选项 12、使用mount选项 五、关于本文 六、相关文档 +++++++++++++++++++++++++++++++++++++++++++++++++ 正文 +++++++++++++++++++++++++++++++++++++++++++++++++ 版权声明 本文是zhyfly兄贴在LinuxSir.Org 的一个帖子而整理出来的，如果您对版权有疑问，请在本帖后面跟帖。谢谢；本文的HTML版本由北南南北整理；修改了整篇文档的全角及说明文字中的单词中每个字母空格的问题；为标题加了编号，方便大家阅读； 前言：关于find命令 由于find具有强大的功能，所以它的选项也很多，其中大部分选项都值得我们花时间来了解一下。即使系统中含有网络文件系统( NFS)，find命令在该文件系统中同样有效，只你具有相应的权限。 在运行一个非常消耗资源的find命令时，很多人都倾向于把它放在后台执行，因为遍历一个大的文件系统可能会花费很长的时间(这里是指30G字节以上的文件系统)。 一、find 命令格式 1、find命令的一般形式为； find pathname [...]]]></description>
			<content:encoded><![CDATA[<p><strong>PS:find是个很强大的命令，能够匹配正则，查找对应权限，能够帮你精确的定位你的系统中的任何地方任何目录下的文件。下面这篇是关于find命令用法最详细的一篇了，记录下。还有要注意的：本文的分号；其实都是带转义字符的\\；，可能是本博的模板问题，后台显示但前台没显示出来。对于不同的系统，直接使用分号可能会有不同的意义，使用转义符\\在分号前明确说明。</strong></p>
<p><strong>总结：</strong>zhy2111314<br />
<strong>来自：</strong>LinuxSir.Org<br />
<strong>整理：</strong>北南南北<br />
<strong>摘要：</strong> 本文是find命令的详细说明，可贵的是针对参数举了很多的实例，大量的例证，让初学者更为容易理解；本文是zhyfly兄贴在论坛中；我对本文进行了再次整理，为方便大家阅读。</p>
<p><strong>目录</strong></p>
<p><strong>版权声明</strong></p>
<p><strong>前言：关于find命令</strong></p>
<p><strong>一、find 命令格式</strong><br />
1、find命令的一般形式为；<br />
2、find命令的参数；<br />
3、find命令选项；<br />
4、使用exec或ok来执行shell命令；</p>
<p><strong>二、find命令的例子；</strong></p>
<p>1、查找当前用户主目录下的所有文件；<br />
2、为了在当前目录中文件属主具有读、写权限，并且文件所属组的用户和其他用户具有读权限的文件；<br />
3、为了查找系统中所有文件长度为0的普通文件，并列出它们的完整路径；<br />
4、查找/var/logs目录中更改时间在7日以前的普通文件，并在删除之前询问它们；<br />
5、为了查找系统中所有属于root组的文件；<br />
6、find命令将删除当目录中访问时间在7日以来、含有数字后缀的admin.log文件<br />
7、为了查找当前文件系统中的所有目录并排序；<br />
8、为了查找系统中所有的rmt磁带设备；</p>
<p><strong>三、xargs</strong></p>
<p><strong>四、find 命令的参数；</strong></p>
<p>1、使用name选项<br />
2、用perm选项<br />
3、忽略某个目录<br />
4、使用find查找文件的时候怎么避开某个文件目录<br />
5、使用user和nouser选项<br />
6、使用group和nogroup选项<br />
7、按照更改时间或访问时间等查找文件<br />
8、查找比某个文件新或旧的文件<br />
9、使用type选项<br />
10、使用size选项<br />
11、使用depth选项<br />
12、使用mount选项</p>
<p><strong>五、关于本文</strong><br />
<strong>六、相关文档</strong><br />
<span id="more-4571"></span><br />
<strong><br />
+++++++++++++++++++++++++++++++++++++++++++++++++<br />
正文<br />
+++++++++++++++++++++++++++++++++++++++++++++++++<br />
</strong></p>
<p><strong>版权声明</strong></p>
<p>本文是zhyfly兄贴在LinuxSir.Org 的一个帖子而整理出来的，如果您对版权有疑问，请在本帖后面跟帖。谢谢；本文的HTML版本由北南南北整理；修改了整篇文档的全角及说明文字中的单词中每个字母空格的问题；为标题加了编号，方便大家阅读；</p>
<p><strong>前言：关于find命令</strong></p>
<p>由于find具有强大的功能，所以它的选项也很多，其中大部分选项都值得我们花时间来了解一下。即使系统中含有网络文件系统( NFS)，find命令在该文件系统中同样有效，只你具有相应的权限。</p>
<p>在运行一个非常消耗资源的find命令时，很多人都倾向于把它放在后台执行，因为遍历一个大的文件系统可能会花费很长的时间(这里是指30G字节以上的文件系统)。</p>
<p><strong><br />
一、find 命令格式<br />
</strong></p>
<p><strong><br />
1、find命令的一般形式为；<br />
</strong></p>
<pre>find pathname -options [-print -exec -ok ...]</pre>
<p><strong><br />
2、find命令的参数；<br />
</strong></p>
<pre>pathname: find命令所查找的目录路径。例如用.来表示当前目录，用/来表示系统根目录。
-print： find命令将匹配的文件输出到标准输出。
-exec： find命令对匹配的文件执行该参数所给出的shell命令。相应命令的形式为'command' {} \\;，注意{}和\\；之间的空格。
-ok： 和-exec的作用相同，只不过以一种更为安全的模式来执行该参数所给出的shell命令，在执行每一个命令之前，都会给出提示，让用户来确定是否执行。</pre>
<p><strong><br />
3、find命令选项<br />
</strong></p>
<pre>-name

按照文件名查找文件。

-perm
按照文件权限来查找文件。

-prune
使用这一选项可以使find命令不在当前指定的目录中查找，如果同时使用-depth选项，那么-prune将被find命令忽略。

-user
按照文件属主来查找文件。

-group
按照文件所属的组来查找文件。

-mtime -n +n
按照文件的更改时间来查找文件， - n表示文件更改时间距现在n天以内，+ n表示文件更改时间距现在n天以前。find命令还有-atime和-ctime 选项，但它们都和-m time选项。

-nogroup
查找无有效所属组的文件，即该文件所属的组在/etc/groups中不存在。

-nouser
查找无有效属主的文件，即该文件的属主在/etc/passwd中不存在。
-newer file1 ! file2

查找更改时间比文件file1新但比文件file2旧的文件。
-type

查找某一类型的文件，诸如：

b - 块设备文件。
d - 目录。
c - 字符设备文件。
p - 管道文件。
l - 符号链接文件。
f - 普通文件。

-size n：[c] 查找文件长度为n块的文件，带有c时表示文件长度以字节计。
-depth：在查找文件时，首先查找当前目录中的文件，然后再在其子目录中查找。
-fstype：查找位于某一类型文件系统中的文件，这些文件系统类型通常可以在配置文件/etc/fstab中找到，该配置文件中包含了本系统中有关文件系统的信息。

-mount：在查找文件时不跨越文件系统mount点。
-follow：如果find命令遇到符号链接文件，就跟踪至链接所指向的文件。
-cpio：对匹配的文件使用cpio命令，将这些文件备份到磁带设备中。</pre>
<p>另外,下面三个的区别:</p>
<pre>   -amin n
查找系统中最后N分钟访问的文件

-atime n
查找系统中最后n*24小时访问的文件

-cmin n
查找系统中最后N分钟被改变文件状态的文件

-ctime n
查找系统中最后n*24小时被改变文件状态的文件

-mmin n
查找系统中最后N分钟被改变文件数据的文件

-mtime n
查找系统中最后n*24小时被改变文件数据的文件</pre>
<p><strong><br />
4、使用exec或ok来执行shell命令<br />
</strong></p>
<p>使用find时，只要把想要的操作写在一个文件里，就可以用exec来配合find查找，很方便的</p>
<p>在有些操作系统中只允许-exec选项执行诸如l s或ls -l这样的命令。大多数用户使用这一选项是为了查找旧文件并删除它们。建议在真正执行rm命令删除文件之前，最好先用ls命令看一下，确认它们是所要删除的文件。</p>
<p>exec选项后面跟随着所要执行的命令或脚本，然后是一对儿{ }，一个空格和一个\\，最后是一个分号。为了使用exec选项，必须要同时使用print选项。如果验证一下find命令，会发现该命令只输出从当前路径起的相对路径及文件名。</p>
<p>例如：为了用ls -l命令列出所匹配到的文件，可以把ls -l命令放在find命令的-exec选项中</p>
<pre># find . -type f -exec ls -l {} \\;
-rw-r--r--    1 root     root        34928 2003-02-25  ./conf/httpd.conf
-rw-r--r--    1 root     root        12959 2003-02-25  ./conf/magic
-rw-r--r--    1 root     root          180 2003-02-25  ./conf.d/README</pre>
<p>上面的例子中，find命令匹配到了当前目录下的所有普通文件，并在-exec选项中使用ls -l命令将它们列出。<br />
在/logs目录中查找更改时间在5日以前的文件并删除它们：</p>
<pre>$ find logs -type f -mtime +5 -exec rm {} \\;</pre>
<p><strong>记住：</strong>在shell中用任何方式删除文件之前，应当先查看相应的文件，一定要小心！当使用诸如mv或rm命令时，可以使用-exec选项的安全模式。它将在对每个匹配到的文件进行操作之前提示你。</p>
<p>在下面的例子中， find命令在当前目录中查找所有文件名以.LOG结尾、更改时间在5日以上的文件，并删除它们，只不过在删除之前先给出提示。</p>
<pre>$ find . -name "*.log"  -mtime +5 -ok rm {} \\;
&lt; rm ... ./conf/httpd.conf &gt; ? n</pre>
<p>按y键删除文件，按n键不删除。</p>
<p>任何形式的命令都可以在-exec选项中使用。</p>
<p>在下面的例子中我们使用grep命令。find命令首先匹配所有文件名为“ passwd*”的文件，例如passwd、passwd.old、passwd.bak，然后执行grep命令看看在这些文件中是否存在一个sam用户。</p>
<pre># find /etc -name "passwd*" -exec grep "sam" {} \\;
sam:x:501:501::/usr/sam:/bin/bash</pre>
<p><strong><br />
二、find命令的例子；<br />
</strong></p>
<p><strong><br />
1、查找当前用户主目录下的所有文件：<br />
</strong></p>
<p>下面两种方法都可以使用</p>
<pre>$ find $HOME -print
$ find ~ -print</pre>
<p><strong><br />
2、让当前目录中文件属主具有读、写权限，并且文件所属组的用户和其他用户具有读权限的文件；<br />
</strong></p>
<pre>$ find . -type f -perm 644 -exec ls -l {} \\;</pre>
<p><strong><br />
3、为了查找系统中所有文件长度为0的普通文件，并列出它们的完整路径；<br />
</strong></p>
<pre>$ find / -type f -size 0 -exec ls -l {} \\;</pre>
<p><strong><br />
4、查找/var/logs目录中更改时间在7日以前的普通文件，并在删除之前询问它们；<br />
</strong></p>
<pre>$ find /var/logs -type f -mtime +7 -ok rm {} \\;</pre>
<p><strong><br />
5、为了查找系统中所有属于root组的文件；<br />
</strong></p>
<pre>$find . -group root -exec ls -l {} \\;
-rw-r--r--    1 root     root          595 10月 31 01:09 ./fie1</pre>
<p><strong><br />
6、find命令将删除当目录中访问时间在7日以来、含有数字后缀的admin.log文件。<br />
</strong></p>
<p>该命令只检查三位数字，所以相应文件的后缀不要超过999。先建几个admin.log*的文件 ，才能使用下面这个命令</p>
<pre>$ find . -name "admin.log[0-9][0-9][0-9]" -atime -7  -ok
rm {} \\;
&lt; rm ... ./admin.log001 &gt; ? n
&lt; rm ... ./admin.log002 &gt; ? n
&lt; rm ... ./admin.log042 &gt; ? n
&lt; rm ... ./admin.log942 &gt; ? n</pre>
<p><strong><br />
7、为了查找当前文件系统中的所有目录并排序；<br />
</strong></p>
<pre>$ find . -type d | sort</pre>
<p><strong><br />
8、为了查找系统中所有的rmt磁带设备；<br />
</strong></p>
<pre>$ find /dev/rmt -print</pre>
<p><strong><br />
三、xargs<br />
</strong></p>
<p>xargs &#8211; build and execute command lines from standard input</p>
<p>在使用find命令的-exec选项处理匹配到的文件时， find命令将所有匹配到的文件一起传递给exec执行。但有些系统对能够传递给exec的命令长度有限制，这样在find命令运行几分钟之后，就会出现溢出错误。错误信息通常是“参数列太长”或“参数列溢出”。这就是xargs命令的用处所在，特别是与find命令一起使用。</p>
<p>find命令把匹配到的文件传递给xargs命令，而xargs命令每次只获取一部分文件而不是全部，不像-exec选项那样。这样它可以先处理最先获取的一部分文件，然后是下一批，并如此继续下去。</p>
<p>在有些系统中，使用-exec选项会为处理每一个匹配到的文件而发起一个相应的进程，并非将匹配到的文件全部作为参数一次执行；这样在有些情况下就会出现进程过多，系统性能下降的问题，因而效率不高；</p>
<p>而使用xargs命令则只有一个进程。另外，在使用xargs命令时，究竟是一次获取所有的参数，还是分批取得参数，以及每一次获取参数的数目都会根据该命令的选项及系统内核中相应的可调参数来确定。</p>
<p>来看看xargs命令是如何同find命令一起使用的，并给出一些例子。</p>
<p>下面的例子查找系统中的每一个普通文件，然后使用xargs命令来测试它们分别属于哪类文件</p>
<pre>#find . -type f -print | xargs file
./.kde/Autostart/Autorun.desktop: UTF-8 Unicode English text
./.kde/Autostart/.directory:      ISO-8859 text\\
......</pre>
<p>在整个系统中查找内存信息转储文件(core dump) ，然后把结果保存到/tmp/core.log 文件中：</p>
<pre>$ find / -name "core" -print | xargs echo "" &gt;/tmp/core.log</pre>
<p>上面这个执行太慢，我改成在当前目录下查找</p>
<pre>#find . -name "file*" -print | xargs echo "" &gt; /temp/core.log
# cat /temp/core.log
./file6</pre>
<p>在当前目录下查找所有用户具有读、写和执行权限的文件，并收回相应的写权限：</p>
<pre># ls -l
drwxrwxrwx    2 sam      adm          4096 10月 30 20:14 file6
-rwxrwxrwx    2 sam      adm             0 10月 31 01:01 http3.conf
-rwxrwxrwx    2 sam      adm             0 10月 31 01:01 httpd.conf

# find . -perm -7 -print | xargs chmod o-w
# ls -l
drwxrwxr-x    2 sam      adm          4096 10月 30 20:14 file6
-rwxrwxr-x    2 sam      adm             0 10月 31 01:01 http3.conf
-rwxrwxr-x    2 sam      adm             0 10月 31 01:01 httpd.conf</pre>
<p>用grep命令在所有的普通文件中搜索hostname这个词：</p>
<pre># find . -type f -print | xargs grep "hostname"
./httpd1.conf:#     different IP addresses or hostnames and have them handled by the
./httpd1.conf:# VirtualHost: If you want to maintain multiple domains/hostnames
on your</pre>
<p>用grep命令在当前目录下的所有普通文件中搜索hostnames这个词：</p>
<pre># find . -name \\* -type f -print | xargs grep "hostnames"
./httpd1.conf:#     different IP addresses or hostnames and have them handled by the
./httpd1.conf:# VirtualHost: If you want to maintain multiple domains/hostnames
on your</pre>
<p>注意，在上面的例子中， \\用来取消find命令中的*在shell中的特殊含义。</p>
<p>find命令配合使用exec和xargs可以使用户对所匹配到的文件执行几乎所有的命令。</p>
<p><strong><br />
四、find 命令的参数<br />
</strong></p>
<p>下面是find一些常用参数的例子，有用到的时候查查就行了，像上面前几个贴子，都用到了其中的的一些参数，也可以用man或查看论坛里其它贴子有find的命令手册</p>
<p><strong><br />
1、使用name选项<br />
</strong></p>
<p>文件名选项是find命令最常用的选项，要么单独使用该选项，要么和其他选项一起使用。</p>
<p>可以使用某种文件名模式来匹配文件，记住要用引号将文件名模式引起来。</p>
<p>不管当前路径是什么，如果想要在自己的根目录$HOME中查找文件名符合*.txt的文件，使用~作为 &#8216;pathname&#8217;参数，波浪号~代表了你的$HOME目录。</p>
<pre>$ find ~ -name "*.txt" -print</pre>
<p>想要在当前目录及子目录中查找所有的‘ *.txt’文件，可以用：</p>
<pre>$ find . -name "*.txt" -print</pre>
<p>想要的当前目录及子目录中查找文件名以一个大写字母开头的文件，可以用：</p>
<pre>$ find . -name "[A-Z]*" -print</pre>
<p>想要在/etc目录中查找文件名以host开头的文件，可以用：</p>
<pre>$ find /etc -name "host*" -print</pre>
<p>想要查找$HOME目录中的文件，可以用：</p>
<pre>$ find ~ -name "*" -print 或find . -print</pre>
<p>要想让系统高负荷运行，就从根目录开始查找所有的文件。</p>
<pre>$ find / -name "*" -print</pre>
<p>如果想在当前目录查找文件名以两个小写字母开头，跟着是两个数字，最后是.txt的文件，下面的命令就能够返回名为ax37.txt的文件：</p>
<pre>$find . -name "[a-z][a-z][0--9][0--9].txt" -print</pre>
<p><strong><br />
2、用perm选项<br />
</strong></p>
<p>按照文件权限模式用-perm选项,按文件权限模式来查找文件的话。最好使用八进制的权限表示法。</p>
<p>如在当前目录下查找文件权限位为755的文件，即文件属主可以读、写、执行，其他用户可以读、执行的文件，可以用：</p>
<pre>$ find . -perm 755 -print</pre>
<p>还有一种表达方法：在八进制数字前面要加一个横杠-，表示都匹配，如-007就相当于777，-006相当于666</p>
<pre># ls -l
-rwxrwxr-x    2 sam      adm             0 10月 31 01:01 http3.conf
-rw-rw-rw-    1 sam      adm         34890 10月 31 00:57 httpd1.conf
-rwxrwxr-x    2 sam      adm             0 10月 31 01:01 httpd.conf
drw-rw-rw-    2 gem      group        4096 10月 26 19:48 sam
-rw-rw-rw-    1 root     root         2792 10月 31 20:19 temp

# find . -perm 006
# find . -perm -006
./sam
./httpd1.conf
./temp</pre>
<p>-perm mode:文件许可正好符合mode</p>
<p>-perm +mode:文件许可部分符合mode</p>
<p>-perm -mode: 文件许可完全符合mode</p>
<p><strong><br />
3、忽略某个目录<br />
</strong></p>
<p>如果在查找文件时希望忽略某个目录，因为你知道那个目录中没有你所要查找的文件，那么可以使用-prune选项来指出需要忽略的目录。在使用-prune选项时要当心，因为如果你同时使用了-depth选项，那么-prune选项就会被find命令忽略。</p>
<p>如果希望在/apps目录下查找文件，但不希望在/apps/bin目录下查找，可以用：</p>
<pre>$ find /apps -path "/apps/bin" -prune -o -print</pre>
<p><strong><br />
4、使用find查找文件的时候怎么避开某个文件目录<br />
</strong></p>
<p>比如要在/usr/sam目录下查找不在dir1子目录之内的所有文件</p>
<pre>find /usr/sam -path "/usr/sam/dir1" -prune -o -print</pre>
<pre>find [-path ..] [expression] 在路径列表的后面的是表达式</pre>
<p>-path “/usr/sam” -prune -o -print 是 -path “/usr/sam” -a -prune -o<br />
-print 的简写表达式按顺序求值, -a 和 -o 都是短路求值，与 shell 的 &amp;&amp; 和 || 类似如果 -path “/usr/sam” 为真，则求值 -prune , -prune 返回真，与逻辑表达式为真；否则不求值 -prune，与逻辑表达式为假。如果 -path “/usr/sam” -a -prune 为假，则求值 -print ，-print返回真，或逻辑表达式为真；否则不求值 -print，或逻辑表达式为真。</p>
<p>这个表达式组合特例可以用伪码写为</p>
<pre>if -path "/usr/sam"  then
-prune
else
-print</pre>
<p>避开多个文件夹</p>
<pre>find /usr/sam \\( -path /usr/sam/dir1 -o -path /usr/sam/file1 \\) -prune -o -print</pre>
<p>圆括号表示表达式的结合。</p>
<pre>\\ 表示引用，即指示 shell 不对后面的字符作特殊解释，而留给 find 命令去解释其意义。</pre>
<p>查找某一确定文件，-name等选项加在-o 之后</p>
<pre>#find /usr/sam  \\(-path /usr/sam/dir1 -o -path /usr/sam/file1 \\) -prune -o -name "temp" -print</pre>
<p><strong><br />
5、使用user和nouser选项<br />
</strong></p>
<p>按文件属主查找文件，如在$HOME目录中查找文件属主为sam的文件，可以用：</p>
<pre>$ find ~ -user sam -print</pre>
<p>在/etc目录下查找文件属主为uucp的文件：</p>
<pre>$ find /etc -user uucp -print</pre>
<p>为了查找属主帐户已经被删除的文件，可以使用-nouser选项。这样就能够找到那些属主在/etc/passwd文件中没有有效帐户的文件。在使用-nouser选项时，不必给出用户名； find命令能够为你完成相应的工作。</p>
<p>例如，希望在/home目录下查找所有的这类文件，可以用：</p>
<pre>$ find /home -nouser -print</pre>
<p><strong><br />
6、使用group和nogroup选项<br />
</strong></p>
<p>就像user和nouser选项一样，针对文件所属于的用户组， find命令也具有同样的选项，为了在/apps目录下查找属于gem用户组的文件，可以用：</p>
<pre>$ find /apps -group gem -print</pre>
<p>要查找没有有效所属用户组的所有文件，可以使用nogroup选项。下面的find命令从文件系统的根目录处查找这样的文件</p>
<pre>$ find / -nogroup-print</pre>
<p><strong><br />
7、按照更改时间或访问时间等查找文件<br />
</strong></p>
<p>如果希望按照更改时间来查找文件，可以使用mtime,atime或ctime选项。如果系统突然没有可用空间了，很有可能某一个文件的长度在此期间增长迅速，这时就可以用mtime选项来查找这样的文件。</p>
<p>用减号-来限定更改时间在距今n日以内的文件，而用加号+来限定更改时间在距今n日以前的文件。</p>
<p>希望在系统根目录下查找更改时间在5日以内的文件，可以用：</p>
<pre>$ find / -mtime -5 -print</pre>
<p>为了在/var/adm目录下查找更改时间在3日以前的文件，可以用：</p>
<pre>$ find /var/adm -mtime +3 -print</pre>
<p><strong><br />
8、查找比某个文件新或旧的文件<br />
</strong></p>
<p>如果希望查找更改时间比某个文件新但比另一个文件旧的所有文件，可以使用-newer选项。它的一般形式为：</p>
<pre>newest_file_name ! oldest_file_name</pre>
<p>其中，！是逻辑非符号。</p>
<p>查找更改时间比文件sam新但比文件temp旧的文件：</p>
<p>例：有两个文件</p>
<pre>-rw-r--r--    1 sam      adm             0 10月 31 01:07 fiel
-rw-rw-rw-    1 sam      adm         34890 10月 31 00:57 httpd1.conf
-rwxrwxr-x    2 sam      adm             0 10月 31 01:01 httpd.conf
drw-rw-rw-    2 gem      group        4096 10月 26 19:48 sam
-rw-rw-rw-    1 root     root         2792 10月 31 20:19 temp

# find -newer httpd1.conf  ! -newer temp -ls
1077669    0 -rwxrwxr-x   2 sam      adm             0 10月 31 01:01 ./httpd.conf
1077671    4 -rw-rw-rw-   1 root     root         2792 10月 31 20:19 ./temp
1077673    0 -rw-r--r--   1 sam      adm             0 10月 31 01:07 ./fiel</pre>
<p>查找更改时间在比temp文件新的文件：</p>
<pre>$ find . -newer temp -print</pre>
<p><strong><br />
9、使用type选项<br />
</strong></p>
<p>在/etc目录下查找所有的目录，可以用：</p>
<pre>$ find /etc -type d -print</pre>
<p>在当前目录下查找除目录以外的所有类型的文件，可以用：</p>
<pre>$ find . ! -type d -print</pre>
<p>在/etc目录下查找所有的符号链接文件，可以用</p>
<pre>$ find /etc -type l -print</pre>
<p><strong><br />
10、使用size选项<br />
</strong></p>
<p>可以按照文件长度来查找文件，这里所指的文件长度既可以用块（block）来计量，也可以用字节来计量。以字节计量文件长度的表达形式为N c；以块计量文件长度只用数字表示即可。</p>
<p>在按照文件长度查找文件时，一般使用这种以字节表示的文件长度，在查看文件系统的大小，因为这时使用块来计量更容易转换。<br />
在当前目录下查找文件长度大于1 M字节的文件：</p>
<pre>$ find . -size +1000000c -print</pre>
<p>在/home/apache目录下查找文件长度恰好为100字节的文件：</p>
<pre>$ find /home/apache -size 100c -print</pre>
<p>在当前目录下查找长度超过10块的文件（一块等于512字节）：</p>
<pre>$ find . -size +10 -print</pre>
<p><strong><br />
11、使用depth选项<br />
</strong></p>
<p>在使用find命令时，可能希望先匹配所有的文件，再在子目录中查找。使用depth选项就可以使find命令这样做。这样做的一个原因就是，当在使用find命令向磁带上备份文件系统时，希望首先备份所有的文件，其次再备份子目录中的文件。</p>
<p>在下面的例子中， find命令从文件系统的根目录开始，查找一个名为CON.FILE的文件。</p>
<p>它将首先匹配所有的文件然后再进入子目录中查找。</p>
<pre>$ find / -name "CON.FILE" -depth -print</pre>
<p><strong><br />
12、使用mount选项<br />
</strong></p>
<p>在当前的文件系统中查找文件（不进入其他文件系统），可以使用find命令的mount选项。</p>
<p>从当前目录开始查找位于本文件系统中文件名以XC结尾的文件：</p>
<pre>$ find . -name "*.XC" -mount -print</pre>
<p><strong>五、关于本文 </strong></p>
<p>本文是find 命令的详细说明，可贵的是针对参数举了很多的实例，大量的例证，让初学者更为容易理解；本文是zhy2111314兄贴在论坛中；我对本文进行了再次整理，为方便大家阅读； ── 北南南北</p>
]]></content:encoded>
			<wfw:commentRss>http://www.ha97.com/4571.html/feed</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>Apache与Tomcat的3种连接方式分析</title>
		<link>http://www.ha97.com/4565.html</link>
		<comments>http://www.ha97.com/4565.html#comments</comments>
		<pubDate>Wed, 09 May 2012 09:31:50 +0000</pubDate>
		<dc:creator>谋万世全局者</dc:creator>
				<category><![CDATA[Apache]]></category>
		<category><![CDATA[Linux]]></category>
		<category><![CDATA[Tomcat]]></category>
		<category><![CDATA[UNIX]]></category>
		<category><![CDATA[Web Server]]></category>
		<category><![CDATA[分析]]></category>
		<category><![CDATA[连接方式]]></category>

		<guid isPermaLink="false">http://www.ha97.com/?p=4565</guid>
		<description><![CDATA[首先我们先介绍一下为什么要让 Apache 与 Tomcat 之间进行连接。事实上 Tomcat 本身已经提供了 HTTP 服务，该服务默认的端口是 8080，装好 tomcat 后通过 8080 端口可以直接使用 Tomcat 所运行的应用程序，你也可以将该端口改为 80。 既然 Tomcat 本身已经可以提供这样的服务，我们为什么还要引入 Apache 或者其他的一些专门的 HTTP 服务器呢？原因有下面几个： 1. 提升对静态文件的处理性能 2. 利用 Web 服务器来做负载均衡以及容错 3. 无缝的升级应用程序 这三点对一个 web 网站来说是非常之重要的，我们希望我们的网站不仅是速度快，而且要稳定，不能因为某个 Tomcat 宕机或者是升级程序导致用户访问不了，而能完成这几个功能的、最好的 HTTP 服务器也就只有 apache 的 http server 了，它跟 tomcat 的结合是最紧密和可靠的。 接下来我们介绍三种方法将 apache 和 tomcat 整合在一起。 1、JK 这是最常见的方式，你可以在网上找到很多关于配置JK的网页，当然最全的还是其官方所提供的文档。JK 本身有两个版本分别是 1 [...]]]></description>
			<content:encoded><![CDATA[<p><strong>首先我们先介绍一下为什么要让 Apache 与 Tomcat 之间进行连接。事实上 Tomcat 本身已经提供了 HTTP 服务，该服务默认的端口是 8080，装好 tomcat 后通过 8080 端口可以直接使用 Tomcat 所运行的应用程序，你也可以将该端口改为 80。</strong></p>
<p>既然 Tomcat 本身已经可以提供这样的服务，我们为什么还要引入 Apache 或者其他的一些专门的 HTTP 服务器呢？原因有下面几个：</p>
<p>1. 提升对静态文件的处理性能<br />
2. 利用 Web 服务器来做负载均衡以及容错<br />
3. 无缝的升级应用程序</p>
<p>这三点对一个 web 网站来说是非常之重要的，我们希望我们的网站不仅是速度快，而且要稳定，不能因为某个 Tomcat 宕机或者是升级程序导致用户访问不了，而能完成这几个功能的、最好的 HTTP 服务器也就只有 apache 的 http server 了，它跟 tomcat 的结合是最紧密和可靠的。</p>
<p>接下来我们介绍三种方法将 apache 和 tomcat 整合在一起。</p>
<p><strong>1、JK</strong></p>
<p>这是最常见的方式，你可以在网上找到很多关于配置JK的网页，当然最全的还是其官方所提供的文档。JK 本身有两个版本分别是 1 和 2，目前 1 最新的版本是1.2.35，而版本 2 早已经废弃了，以后不再有新版本的推出了，所以建议你采用版本 1。<br />
<span id="more-4565"></span><br />
JK 是通过 AJP 协议与 Tomcat 服务器进行通讯的，Tomcat 默认的 AJP Connector 的端口是 8009。JK 本身提供了一个监控以及管理的页面 jkstatus，通过 jkstatus 可以监控 JK 目前的工作状态以及对到 tomcat 的连接进行设置，如下图所示：<br />
<a name="N10066"></a><strong>图 1：监控以及管理的页面 jkstatus</strong><br />
<img src="http://www.ha97.com/wp-content/uploads/image/2012/05/093150T30.jpg" alt="图 1：监控以及管理的页面 jkstatus" width="554" height="228" border="0" /></p>
<p>在这个图中我们可以看到当前JK配了两个连接分别到 8109 和 8209 端口上，目前 s2 这个连接是停止状态，而 s1 这个连接自上次重启后已经处理了 47 万多个请求，流量达到 6.2 个 G，最大的并发数有 13 等等。我们也可以利用 jkstatus 的管理功能来切换 JK 到不同的 Tomcat 上，例如将 s2 启用，并停用 s1，这个在更新应用程序的时候非常有用，而且整个切换过程对用户来说是透明的，也就达到了无缝升级的目的。关于 JK 的配置文章网上已经非常多了，这里我们不再详细的介绍整个配置过程，但我要讲一下配置的思路，只要明白了配置的思路，JK 就是一个非常灵活的组件。</p>
<p>JK 的配置最关键的有三个文件，分别是</p>
<p><strong>httpd.conf </strong><br />
Apache 服务器的配置文件，用来加载 JK 模块以及指定 JK 配置文件信息</p>
<p><strong>workers.properties</strong><br />
到 Tomcat 服务器的连接定义文件</p>
<p><strong>uriworkermap.properties</strong><br />
URI 映射文件，用来指定哪些 URL 由 Tomcat 处理，你也可以直接在 httpd.conf 中配置这些 URI，但是独立这些配置的好处是 JK 模块会定期更新该文件的内容，使得我们修改配置的时候无需重新启动 Apache 服务器。</p>
<p>其中第二、三个配置文件名都可以自定义。下面是一个典型的 httpd.conf 对 JK 的配置</p>
<pre># (httpd.conf)
# 加载 mod_jk 模块
LoadModule jk_module modules/mod_jk.so

#
# Configure mod_jk
#

JkWorkersFile conf/workers.properties
JkMountFile conf/uriworkermap.properties
JkLogFile logs/mod_jk.log
JkLogLevel warn</pre>
<p>接下来我们在 Apache 的 conf 目录下新建两个文件分别是 workers.properties、uriworkermap.properties。这两个文件的内容大概如下</p>
<pre>#
# workers.properties
#

# list the workers by name

worker.list=DLOG4J, status

# localhost server 1
# ------------------------
worker.s1.port=8109
worker.s1.host=localhost
worker.s1.type=ajp13

# localhost server 2
# ------------------------
worker.s2.port=8209
worker.s2.host=localhost
worker.s2.type=ajp13
worker.s2.stopped=1

worker.DLOG4J.type=lb
worker.retries=3
worker.DLOG4J.balanced_workers=s1, s2
worker.DLOG4J.sticky_session=1

worker.status.type=status</pre>
<p>以上的 workers.properties 配置就是我们前面那个屏幕抓图的页面所用的配置。首先我们配置了两个类型为 ajp13 的 worker 分别是 s1 和 s2，它们指向同一台服务器上运行在两个不同端口 8109 和 8209 的 Tomcat 上。接下来我们配置了一个类型为 lb（也就是负载均衡的意思）的 worker，它的名字是 DLOG4J，这是一个逻辑的 worker，它用来管理前面配置的两个物理连接 s1 和 s2。最后还配置了一个类型为 status 的 worker，这是用来监控 JK 本身的模块。有了这三个 worker 还不够，我们还需要告诉 JK，哪些 worker 是可用的，所以就有 <strong>worker.list = DLOG4J, status</strong> 这行配置。</p>
<p>接下来便是 URI 的映射配置了，我们需要指定哪些链接是由 Tomcat 处理的，哪些是由 Apache 直接处理的，看看下面这个文件你就能明白其中配置的意义</p>
<pre>/*=DLOG4J
/jkstatus=status

!/*.gif=DLOG4J
!/*.jpg=DLOG4J
!/*.png=DLOG4J
!/*.css=DLOG4J
!/*.js=DLOG4J
!/*.htm=DLOG4J
!/*.html=DLOG4J</pre>
<p>相信你已经明白了一大半了：所有的请求都由 DLOG4J 这个 worker 进行处理，但是有几个例外，/jkstatus 请求由 status 这个 worker 处理。另外这个配置中每一行数据前面的感叹号是什么意思呢？感叹号表示接下来的 URI 不要由 JK 进行处理，也就是 Apache 直接处理所有的图片、css 文件、js 文件以及静态 html 文本文件。</p>
<p>通过对 workers.properties 和 uriworkermap.properties 的配置，可以有各种各样的组合来满足我们前面提出对一个 web 网站的要求。您不妨动手试试！</p>
<p><strong>2、http_proxy</strong></p>
<p>这是利用 Apache 自带的 mod_proxy 模块使用代理技术来连接 Tomcat。在配置之前请确保是否使用的是 2.2.x 版本的 Apache 服务器。因为 2.2.x 版本对这个模块进行了重写，大大的增强了其功能和稳定性。</p>
<p>http_proxy 模式是基于 HTTP 协议的代理，因此它要求 Tomcat 必须提供 HTTP 服务，也就是说必须启用 Tomcat 的 HTTP Connector。一个最简单的配置如下</p>
<pre>ProxyPass /images !
ProxyPass /css !
ProxyPass /js !
ProxyPass / http://localhost:8080/</pre>
<p>在这个配置中，我们把所有 http://localhost 的请求代理到 http://localhost:8080/ ，这也就是 Tomcat 的访问地址，除了 images、css、js 几个目录除外。我们同样可以利用 mod_proxy 来做负载均衡，再看看下面这个配置</p>
<pre>ProxyPass /images !
ProxyPass /css !
ProxyPass /js !

ProxyPass / balancer://example/
&lt;Proxy balancer://example/&gt;
BalancerMember http://server1:8080/
BalancerMember http://server2:8080/
BalancerMember http://server3:8080/
&lt;/Proxy&gt;</pre>
<p>配置比 JK 简单多了，而且它也可以通过一个页面来监控集群运行的状态，并做一些简单的维护设置。<br />
<a name="N100EA"></a><strong>图 2：监控集群运行状态</strong><br />
<img src="http://www.ha97.com/wp-content/uploads/image/2012/05/093151GIl.gif" alt="图 2：监控集群运行状态" width="553" height="427" border="0" /></p>
<p><strong>3、ajp_proxy</strong></p>
<p>ajp_proxy 连接方式其实跟 http_proxy 方式一样，都是由 mod_proxy 所提供的功能。配置也是一样，只需要把 http:// 换成 ajp:// ，同时连接的是 Tomcat 的 AJP Connector 所在的端口。上面例子的配置可以改为：</p>
<pre>ProxyPass /images !
ProxyPass /css !
ProxyPass /js !

ProxyPass / balancer://example/
&lt;Proxy balancer://example/&gt;
BalancerMember ajp://server1:8080/
BalancerMember ajp://server2:8080/
BalancerMember ajp://server3:8080/
&lt;/Proxy&gt;</pre>
<p>采用 proxy 的连接方式，需要在 Apache 上加载所需的模块，mod_proxy 相关的模块有 mod_proxy.so、mod_proxy_connect.so、mod_proxy_http.so、mod_proxy_ftp.so、mod_proxy_ajp.so， 其中 mod_proxy_ajp.so 只在 Apache 2.2.x 中才有。如果是采用 http_proxy 方式则需要加载 mod_proxy.so 和 mod_proxy_http.so；如果是 ajp_proxy 则需要加载 mod_proxy.so 和 mod_proxy_ajp.so这两个模块。</p>
<p><strong><a name="N1010F"></a>比较总结</strong></p>
<p>相对于 JK 的连接方式，后两种在配置上是比较简单的，灵活性方面也一点都不逊色。但就稳定性而言就不像 JK 这样久经考验，毕竟 Apache 2.2.3 推出的时间并不长，采用这种连接方式的网站还不多，因此，如果是应用于关键的互联网网站，还是建议采用 JK 的连接方式。</p>
<p>参考资料</p>
<ul>
<li>获得 <a rel="nofollow" target="_blank" href="http://httpd.apache.org/">Apache Http Server</a>。</li>
<li>获得 <a rel="nofollow" target="_blank" href="http://tomcat.apache.org/">Apache Tomcat</a>。</li>
<li><a rel="nofollow" target="_blank" href="http://tomcat.apache.org/connectors-doc/">JK 文档</a>。</li>
</ul>
<p>作者：刘冬，J2EE/J2ME开发工程师。国内知名开源社区www.oschina.net创始人。<br />
转自：http://www.ibm.com/developerworks/cn/opensource/os-lo-apache-tomcat/</p>
]]></content:encoded>
			<wfw:commentRss>http://www.ha97.com/4565.html/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>百度的Hadoop分布式文件系统图解：4000节点集群</title>
		<link>http://www.ha97.com/4553.html</link>
		<comments>http://www.ha97.com/4553.html#comments</comments>
		<pubDate>Wed, 09 May 2012 04:28:58 +0000</pubDate>
		<dc:creator>谋万世全局者</dc:creator>
				<category><![CDATA[Linux]]></category>
		<category><![CDATA[UNIX]]></category>
		<category><![CDATA[互联网]]></category>
		<category><![CDATA[服务器架构]]></category>
		<category><![CDATA[虚拟化与云计算]]></category>
		<category><![CDATA[Hadoop]]></category>
		<category><![CDATA[分布式系统]]></category>
		<category><![CDATA[图解]]></category>
		<category><![CDATA[百度]]></category>
		<category><![CDATA[节点]]></category>
		<category><![CDATA[集群]]></category>

		<guid isPermaLink="false">http://www.ha97.com/?p=4553</guid>
		<description><![CDATA[在 NoSQL 方面，之前了解到百度对 Hadoop 和 hypertable 都有研究，而且 hypertable 方面更是作为其主要赞助商之一，但之前和百度的一些朋友了解到百度内部对 hypertable 倒是使用不多，相反在 Hadoop 方面倒是有比较大的应用实例。下面一篇文章描述了百度内部4000个结点的 Hadoop 集群的一些技术细节。 百度的高性能计算系统(主要是后端数据训练和计算)目前有4000节点，超过10个的集群，最大的集群规模在1000个节点以上。每个节点由8核CPU以及16G内存以及12TB硬盘组成，每天的数据生成量在3PB以上。规划当中的架构将有超过1万个节点，每天的数据生成量在10PB以上。 底层的计算资源管理层采用了Agent调度不同类型的计算分别给MPI结构的算法和Map-Reduce和DAG算法应用等。而通过调度的分配，可以让HPC高性能计算集群和大规模分布式集群各得其所的计算相应数据。 百度通过HCE对streaming作业的排序，压缩，解压缩，内存控制进行了优化并提供了C++版的MapReduce接口。 百度HCE语言的有关内容，HCE是基于C++的Hadoop环境，是一个全功能C++环境，可以避开Java语言对于释放内存和资源申请的弊端，并在调用数据时绕开Java语言的所有关节，极大的提升算法效率。 百度的调度器是在capacity-scheduler的基础上根据自身业务改进的。 百度计划对shuffle流程进行大幅改造 转自：http://www.cnblogs.com/chinacloud/archive/2010/11/08/1871592.html Hadoop的知名应用项目请参考： http://wiki.apache.org/hadoop/PoweredBy]]></description>
			<content:encoded><![CDATA[<p>在 NoSQL 方面，之前了解到百度对 Hadoop 和 hypertable 都有研究，而且 hypertable 方面更是作为其主要赞助商之一，但之前和百度的一些朋友了解到百度内部对 hypertable 倒是使用不多，相反在 Hadoop 方面倒是有比较大的应用实例。下面一篇文章描述了百度内部4000个结点的 Hadoop 集群的一些技术细节。</p>
<p>百度的高性能计算系统(主要是后端数据训练和计算)目前有4000节点，超过10个的集群，最大的集群规模在1000个节点以上。每个节点由8核CPU以及16G内存以及12TB硬盘组成，每天的数据生成量在3PB以上。规划当中的架构将有超过1万个节点，每天的数据生成量在10PB以上。</p>
<p><img src="http://www.ha97.com/wp-content/uploads/image/2012/05/042910eSr.jpg" alt="" width="450" height="275" border="0" /><br />
<span id="more-4553"></span><br />
底层的计算资源管理层采用了Agent调度不同类型的计算分别给MPI结构的算法和Map-Reduce和DAG算法应用等。而通过调度的分配，可以让HPC高性能计算集群和大规模分布式集群各得其所的计算相应数据。</p>
<p><img src="http://www.ha97.com/wp-content/uploads/image/2012/05/0429132cG.jpg" alt="" width="450" height="311" border="0" /></p>
<p>百度通过HCE对streaming作业的排序，压缩，解压缩，内存控制进行了优化并提供了C++版的MapReduce接口。</p>
<p><img src="http://www.ha97.com/wp-content/uploads/image/2012/05/0429160YV.jpg" alt="" width="500" height="334" border="0" /></p>
<p><img src="http://www.ha97.com/wp-content/uploads/image/2012/05/042919enl.jpg" alt="" width="500" height="275" border="0" /></p>
<p><img src="http://www.ha97.com/wp-content/uploads/image/2012/05/042923Ivi.jpg" alt="" width="527" height="286" border="0" /></p>
<p><img src="http://www.ha97.com/wp-content/uploads/image/2012/05/042927GaA.jpg" alt="" width="500" height="381" border="0" /></p>
<p>百度HCE语言的有关内容，HCE是基于C++的Hadoop环境，是一个全功能C++环境，可以避开Java语言对于释放内存和资源申请的弊端，并在调用数据时绕开Java语言的所有关节，极大的提升算法效率。</p>
<p><img src="http://www.ha97.com/wp-content/uploads/image/2012/05/042930DTV.jpg" alt="" width="500" height="324" border="0" /></p>
<p>百度的调度器是在capacity-scheduler的基础上根据自身业务改进的。</p>
<p><img src="http://www.ha97.com/wp-content/uploads/image/2012/05/042934aAR.jpg" alt="" width="500" height="311" border="0" /></p>
<p>百度计划对shuffle流程进行大幅改造</p>
<p>转自：http://www.cnblogs.com/chinacloud/archive/2010/11/08/1871592.html</p>
<p>Hadoop的知名应用项目请参考：</p>
<p>http://wiki.apache.org/hadoop/PoweredBy</p>
]]></content:encoded>
			<wfw:commentRss>http://www.ha97.com/4553.html/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Linux抓包工具tcpdump详解</title>
		<link>http://www.ha97.com/4550.html</link>
		<comments>http://www.ha97.com/4550.html#comments</comments>
		<pubDate>Tue, 08 May 2012 08:45:17 +0000</pubDate>
		<dc:creator>谋万世全局者</dc:creator>
				<category><![CDATA[Linux]]></category>
		<category><![CDATA[Tools]]></category>
		<category><![CDATA[UNIX]]></category>
		<category><![CDATA[tcpdump]]></category>
		<category><![CDATA[抓包工具]]></category>
		<category><![CDATA[数据包]]></category>
		<category><![CDATA[详解]]></category>

		<guid isPermaLink="false">http://www.ha97.com/?p=4550</guid>
		<description><![CDATA[PS：tcpdump是一个用于截取网络分组，并输出分组内容的工具，简单说就是数据包抓包工具。tcpdump凭借强大的功能和灵活的截取策略，使其成为Linux系统下用于网络分析和问题排查的首选工具。 tcpdump提供了源代码，公开了接口，因此具备很强的可扩展性，对于网络维护和入侵者都是非常有用的工具。tcpdump存在于基本的Linux系统中，由于它需要将网络界面设置为混杂模式，普通用户不能正常执行，但具备root权限的用户可以直接执行它来获取网络上的信息。因此系统中存在网络分析工具主要不是对本机安全的威胁，而是对网络上的其他计算机的安全存在威胁。 一、概述 顾名思义，tcpdump可以将网络中传送的数据包的“头”完全截获下来提供分析。它支持针对网络层、协议、主机、网络或端口的过滤，并提供and、or、not等逻辑语句来帮助你去掉无用的信息。 # tcpdump -vv tcpdump: listening on eth0, link-type EN10MB (Ethernet), capture size 96 bytes 11:53:21.444591 IP (tos 0x10, ttl  64, id 19324, offset 0, flags [DF], proto 6, length: 92) asptest.localdomain.ssh &#62; 192.168.228.244.1858: P 3962132600:3962132652(52) ack 2726525936 win 1266 asptest.localdomain.1077 &#62; 192.168.228.153.domain: [bad udp cksum 166e!]  325+ PTR? 244.228.168.192.in-addr.arpa. (46) 11:53:21.446929 [...]]]></description>
			<content:encoded><![CDATA[<p><strong>PS：tcpdump是一个用于截取网络分组，并输出分组内容的工具，简单说就是数据包抓包工具。tcpdump凭借强大的功能和灵活的截取策略，使其成为Linux系统下用于网络分析和问题排查的首选工具。</strong></p>
<p>tcpdump提供了源代码，公开了接口，因此具备很强的可扩展性，对于网络维护和入侵者都是非常有用的工具。tcpdump存在于基本的Linux系统中，由于它需要将网络界面设置为混杂模式，普通用户不能正常执行，但具备root权限的用户可以直接执行它来获取网络上的信息。因此系统中存在网络分析工具主要不是对本机安全的威胁，而是对网络上的其他计算机的安全存在威胁。</p>
<p><strong>一、概述</strong><br />
顾名思义，tcpdump可以将网络中传送的数据包的“头”完全截获下来提供分析。它支持针对网络层、协议、主机、网络或端口的过滤，并提供and、or、not等逻辑语句来帮助你去掉无用的信息。</p>
<p><code><br />
# tcpdump -vv<br />
tcpdump: listening on eth0, link-type EN10MB (Ethernet), capture size 96 bytes<br />
11:53:21.444591 IP (tos 0x10, ttl  64, id 19324, offset 0, flags [DF], proto 6, length: 92) asptest.localdomain.ssh &gt; 192.168.228.244.1858: P 3962132600:3962132652(52) ack 2726525936 win 1266<br />
asptest.localdomain.1077 &gt; 192.168.228.153.domain: [bad udp cksum 166e!]  325+ PTR? 244.228.168.192.in-addr.arpa. (46)<br />
11:53:21.446929 IP (tos 0x0, ttl  64, id 42911, offset 0, flags [DF], proto 17, length: 151) 192.168.228.153.domain &gt; asptest.localdomain.1077:  325 NXDomain q: PTR? 244.228.168.192.in-addr.arpa. 0/1/0 ns: 168.192.in-addr.arpa. (123)<br />
11:53:21.447408 IP (tos 0x10, ttl  64, id 19328, offset 0, flags [DF], proto 6, length: 172) asptest.localdomain.ssh &gt; 192.168.228.244.1858: P 168:300(132) ack 1 win 1266<br />
347 packets captured<br />
1474 packets received by filter<br />
745 packets dropped by kernel<br />
</code></p>
<p>不带参数的tcpdump会收集网络中所有的信息包头，数据量巨大，必须过滤。</p>
<p><strong>二、选项介绍</strong></p>
<p><span id="more-4550"></span></p>
<blockquote><p>
-A 以ASCII格式打印出所有分组，并将链路层的头最小化。</p>
<p>-c 在收到指定的数量的分组后，tcpdump就会停止。</p>
<p>-C 在将一个原始分组写入文件之前，检查文件当前的大小是否超过了参数file_size 中指定的大小。如果超过了指定大小，则关闭当前文件，然后在打开一个新的文件。参数 file_size 的单位是兆字节（是1,000,000字节，而不是1,048,576字节）。</p>
<p>-d 将匹配信息包的代码以人们能够理解的汇编格式给出。</p>
<p>-dd 将匹配信息包的代码以c语言程序段的格式给出。</p>
<p>-ddd 将匹配信息包的代码以十进制的形式给出。</p>
<p>-D 打印出系统中所有可以用tcpdump截包的网络接口。</p>
<p>-e 在输出行打印出数据链路层的头部信息。</p>
<p>-E 用spi@ipaddr algo:secret解密那些以addr作为地址，并且包含了安全参数索引值spi的IPsec ESP分组。</p>
<p>-f 将外部的Internet地址以数字的形式打印出来。</p>
<p>-F 从指定的文件中读取表达式，忽略命令行中给出的表达式。</p>
<p>-i 指定监听的网络接口。</p>
<p>-l 使标准输出变为缓冲行形式，可以把数据导出到文件。</p>
<p>-L 列出网络接口的已知数据链路。</p>
<p>-m 从文件module中导入SMI MIB模块定义。该参数可以被使用多次，以导入多个MIB模块。</p>
<p>-M 如果tcp报文中存在TCP-MD5选项，则需要用secret作为共享的验证码用于验证TCP-MD5选选项摘要（详情可参考RFC 2385）。</p>
<p>-b 在数据-链路层上选择协议，包括ip、arp、rarp、ipx都是这一层的。</p>
<p>-n 不把网络地址转换成名字。</p>
<p>-nn 不进行端口名称的转换。</p>
<p>-N 不输出主机名中的域名部分。例如，‘nic.ddn.mil‘只输出’nic‘。</p>
<p>-t 在输出的每一行不打印时间戳。</p>
<p>-O 不运行分组分组匹配（packet-matching）代码优化程序。</p>
<p>-P 不将网络接口设置成混杂模式。</p>
<p>-q 快速输出。只输出较少的协议信息。</p>
<p>-r 从指定的文件中读取包(这些包一般通过-w选项产生)。</p>
<p>-S 将tcp的序列号以绝对值形式输出，而不是相对值。</p>
<p>-s 从每个分组中读取最开始的snaplen个字节，而不是默认的68个字节。</p>
<p>-T 将监听到的包直接解释为指定的类型的报文，常见的类型有rpc远程过程调用）和snmp（简单网络管理协议；）。</p>
<p>-t 不在每一行中输出时间戳。</p>
<p>-tt 在每一行中输出非格式化的时间戳。</p>
<p>-ttt 输出本行和前面一行之间的时间差。</p>
<p>-tttt 在每一行中输出由date处理的默认格式的时间戳。</p>
<p>-u 输出未解码的NFS句柄。</p>
<p>-v 输出一个稍微详细的信息，例如在ip包中可以包括ttl和服务类型的信息。</p>
<p>-vv 输出详细的报文信息。</p>
<p>-w 直接将分组写入文件中，而不是不分析并打印出来。
</p></blockquote>
<p><strong>三、tcpdump的表达式介绍</strong></p>
<p>表达式是一个正则表达式，tcpdump利用它作为过滤报文的条件，如果一个报文满足表 达式的条件，则这个报文将会被捕获。如果没有给出任何条件，则网络上所有的信息包 将会被截获。</p>
<p>在表达式中一般如下几种类型的关键字：</p>
<p><strong>第一种</strong>是关于类型的关键字，主要包括host，net，port，例如 host 210.27.48.2， 指明 210.27.48.2是一台主机，net 202.0.0.0指明202.0.0.0是一个网络地址，port 23 指明端口号是23。如果没有指定类型，缺省的类型是host。</p>
<p><strong>第二种</strong>是确定传输方向的关键字，主要包括src，dst，dst or src，dst and src， 这些关键字指明了传输的方向。举例说明，src 210.27.48.2 ，指明ip包中源地址是 210.27.48.2 ， dst net 202.0.0.0 指明目的网络地址是202.0.0.0。如果没有指明 方向关键字，则缺省是src or dst关键字。</p>
<p><strong>第三种</strong>是协议的关键字，主要包括fddi，ip，arp，rarp，tcp，udp等类型。Fddi指明是在FDDI (分布式光纤数据接口网络)上的特定的网络协议，实际上它是”ether”的别名，fddi和ether 具有类似的源地址和目的地址，所以可以将fddi协议包当作ether的包进行处理和分析。 其他的几个关键字就是指明了监听的包的协议内容。如果没有指定任何协议，则tcpdump 将会 监听所有协议的信息包。</p>
<p>除了这三种类型的关键字之外，其他重要的关键字如下：gateway， broadcast，less， greater， 还有三种逻辑运算，取非运算是 ‘not &#8216; &#8216;! ‘， 与运算是’and’，’&amp;&amp;&#8217;;或运算是’or’ ，’&amp;#124;&amp;#124;’； 这些关键字可以组合起来构成强大的组合条件来满足人们的需要。</p>
<p><strong>四、输出结果介绍</strong></p>
<p>下面我们介绍几种典型的tcpdump命令的输出信息</p>
<p><strong>(1) 数据链路层头信息</strong><br />
使用命令：<br />
<code>#tcpdump --e host ICE</code><br />
ICE 是一台装有linux的主机。它的MAC地址是0：90：27：58：AF：1A H219是一台装有Solaris的SUN工作站。它的MAC地址是8：0：20：79：5B：46； 上一条命令的输出结果如下所示：</p>
<p><code>21:50:12.847509 eth0 &lt; 8:0:20:79:5b:46 0:90:27:58:af:1a ip 60: h219.33357 &gt; ICE.  telne t 0:0(0) ack 22535 win 8760 (DF)</code></p>
<p>21：50：12是显示的时间， 847509是ID号，eth0 &lt;表示从网络接口eth0接收该分组， eth0 &gt;表示从网络接口设备发送分组， 8:0:20:79:5b:46是主机H219的MAC地址， 它表明是从源地址H219发来的分组. 0:90:27:58:af:1a是主机ICE的MAC地址， 表示该分组的目的地址是ICE。 ip 是表明该分组是IP分组，60 是分组的长度， h219.33357 &gt; ICE. telnet 表明该分组是从主机H219的33357端口发往主机ICE的 TELNET(23)端口。 ack 22535 表明对序列号是222535的包进行响应。 win 8760表明发 送窗口的大小是8760。</p>
<p><strong>(2) ARP包的tcpdump输出信息</strong></p>
<p>使用命令：<br />
<code>#tcpdump arp</code></p>
<p>得到的输出结果是：</p>
<p><code>22:32:42.802509 eth0 &gt; arp who-has route tell ICE (0:90:27:58:af:1a)<br />
22:32:42.802902 eth0 &lt; arp reply route is-at 0:90:27:12:10:66 (0:90:27:58:af:1a)</code></p>
<p>22:32:42是时间戳， 802509是ID号， eth0 &gt;表明从主机发出该分组，arp表明是ARP请求包， who-has route tell ICE表明是主机ICE请求主机route的MAC地址。 0:90:27:58:af:1a是主机 ICE的MAC地址。</p>
<p><strong>(3) TCP包的输出信息</strong></p>
<p>用tcpdump捕获的TCP包的一般输出信息是：</p>
<p><code>src &gt; dst: flags data-seqno ack window urgent options</code></p>
<p>src &gt; dst:表明从源地址到目的地址， flags是TCP报文中的标志信息，S 是SYN标志， F (FIN)， P (PUSH) ， R (RST) “.” (没有标记); data-seqno是报文中的数据 的顺序号， ack是下次期望的顺序号， window是接收缓存的窗口大小， urgent表明 报文中是否有紧急指针。 Options是选项。</p>
<p><strong>(4) UDP包的输出信息</strong></p>
<p>用tcpdump捕获的UDP包的一般输出信息是：</p>
<p><code>route.port1 &gt; ICE.port2: udp lenth</code></p>
<p>UDP十分简单，上面的输出行表明从主机route的port1端口发出的一个UDP报文 到主机ICE的port2端口，类型是UDP， 包的长度是lenth。</p>
<p><strong>五、举例</strong></p>
<p>(1) 想要截获所有210.27.48.1 的主机收到的和发出的所有的分组：<br />
<code>#tcpdump host 210.27.48.1</code></p>
<p>(2) 想要截获主机210.27.48.1 和主机210.27.48.2或210.27.48.3的通信，使用命令（注意：括号前的反斜杠是必须的）：<br />
<code>#tcpdump host 210.27.48.1 and \(210.27.48.2 or 210.27.48.3 \)</code></p>
<p>(3) 如果想要获取主机210.27.48.1除了和主机210.27.48.2之外所有主机通信的ip包，使用命令：<br />
<code>#tcpdump ip host 210.27.48.1 and ! 210.27.48.2</code></p>
<p>(4) 如果想要获取主机192.168.228.246接收或发出的ssh包，并且不转换主机名使用如下命令：<br />
<code>#tcpdump -nn -n src host 192.168.228.246 and port 22 and tcp</code></p>
<p>(5) 获取主机192.168.228.246接收或发出的ssh包，并把mac地址也一同显示：<br />
<code># tcpdump -e src host 192.168.228.246 and port 22 and tcp -n -nn</code></p>
<p>(6) 过滤的是源主机为192.168.0.1与目的网络为192.168.0.0的报头：<br />
<code>tcpdump src host 192.168.0.1 and dst net 192.168.0.0/24</code></p>
<p>(7) 过滤源主机物理地址为XXX的报头：<br />
<code>tcpdump ether src 00:50:04:BA:9B and dst……</code><br />
（为什么ether src后面没有host或者net？物理地址当然不可能有网络喽）。</p>
<p>(8) 过滤源主机192.168.0.1和目的端口不是telnet的报头，并导入到tes.t.txt文件中：<br />
<code>Tcpdump src host 192.168.0.1 and dst port not telnet -l &gt; test.txt</code></p>
<p><strong>ip icmp arp rarp 和 tcp、udp、icmp这些选项等都要放到第一个参数的位置，用来过滤数据报的类型。</strong></p>
<p><strong>例题：如何使用tcpdump监听来自eth0适配卡且通信协议为port 22，目标来源为192.168.1.100的数据包资料？</strong></p>
<p>答：tcpdump -i eth0 -nn port 22 and src host 192.168.1.100</p>
<p><strong>例题：如何使用tcpdump抓取访问eth0适配卡且访问端口为tcp 9080？</strong></p>
<p>答:tcpdump -i eth0 dst 172.168.70.35 and tcp port 9080</p>
<p><strong>例题：如何使用tcpdump抓取与主机192.168.43.23或着与主机192.168.43.24通信报文，并且显示在控制台上</strong></p>
<p>tcpdump -X -s 1024 -i eth0 host \(192.168.43.23 or 192.168.43.24\) and  host 172.16.70.35</p>
<p>本文来源于Google，原作者不详，本人重新做了排版。</p>
]]></content:encoded>
			<wfw:commentRss>http://www.ha97.com/4550.html/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
	</channel>
</rss>

