<?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>舒の随想日记 &#187; 2011</title>
	<atom:link href="http://blog.hesey.net/2011/feed" rel="self" type="application/rss+xml" />
	<link>http://blog.hesey.net</link>
	<description>思考生活，关注科技。To live is to CHANGE the world.</description>
	<lastBuildDate>Tue, 03 Apr 2012 13:15:33 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.3.2</generator>
		<item>
		<title>回首2011</title>
		<link>http://blog.hesey.net/2011/12/review-my-2011.html?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=review-my-2011</link>
		<comments>http://blog.hesey.net/2011/12/review-my-2011.html#comments</comments>
		<pubDate>Sat, 31 Dec 2011 14:08:54 +0000</pubDate>
		<dc:creator>Hesey</dc:creator>
				<category><![CDATA[思考]]></category>

		<guid isPermaLink="false">http://blog.hesey.net/?p=884</guid>
		<description><![CDATA[如果要用一个词来形容2011的话，我想，就用“激动”好了。 这一年我从学校出来，由一个胆胆颤颤的学生，正式转变成一名工作者，经济上的收入虽然不高，但勉强可以做到自收自支，更多的是，开始把自己的能力发挥出来，能够做一些真正产生影响的东西，这是我一直以来都想做的事情。 这一年，很多在书上看到的技术产品，总算可以真的亲手去用，去体验，去借助我的代码直接触摸那些曾经让我觉得无比高耸的高峰。 这一年，我终于成为团队的一员，从小作坊的独立开发，慢慢适应协同开发、合作，不得不说，和一群相投的人一起做事，真的很开心！ Timeline 3月份到了学校以后，心态比较焦虑，关于实习的事情，一方面自己很想有机会能出去看看，能真的去做一些事情，另一方面，又唯恐自己还达不到要求，做得不好。那段时间一直上自习看书，和考研的兄弟们一起窝在考研自习室钻研。后来正巧朋友飘零发推说招实习生，周末匆匆忙忙准备了一份简历就投过去了，周一下午我还在自习室看分布式，大概四点二十意外接到了面试的电话，全是技术问题，自己觉得答得比较一般了，有两个问题没答得比较顺畅。 晚上七点半的时候，面试官，也就是我现在的老大，打来电话，恭喜我通过面试，正式“邀请”我去淘宝实习（当时觉得，这么一家大公司，对一个学生能用“邀请”这个词，真是很难得）。当时和女朋友坐在公交车上，她知道我当时有多开心。 然后就是完成在学校的任务了，师兄列了一些需要掌握的知识点出来，我就自己看书去学，这个过程中，眼界开阔了许多，也因为更有方向感，能够定下心来深入地去学习一番，这段时间，可以说自己的成长是飞速的。 7月15号终于完成了学校的实训项目，过了一周我就到了公司。 其实刚来的时候，真的很不习惯，首先就是自己会的东西，和现有的事情没法很好地接轨，简单地说，会的用不上，要做的都不会。那段时间真的很纠结，因为自己很想尽快上手去做一些有价值的事情，自己也主动找老大聊了几次，慢慢找到方向感。 后来就是做的第一个项目了，在这个项目中，真正接触到了很多以前只是在文字上见过的东西，例如JNI（为此我还复习了一点C/C++）、HSF、Tair、Hadoop等等，像分布式RPC、分布式缓存、并发一系列的东西，能够说自己用以前自己的理解去用起来，实现了一个高效的异步生产消费模型，充分发挥了机器的能力，把CPU、IO利用率都提升了很多，也成为后来处理海量数据的一个基础设施。 后面越来越熟悉现有的架构、框架，慢慢有些观念也改变了，比如业务和技术，比如对产品的理解，越来越希望能把自己的产品做得更好，思考什么才是更好，用什么样的技术去做。做的很多东西也上线了，真正的对用户产生了影响。 2011年一共写了13篇Blog，数量不多，但质量都还可以，明年希望能产出更多分享的东西。 2011年，成长良多，要感激很多人，身边的师兄们，网上的朋友们，也感谢2011年的自己，很快适应了新的环境，也总算做了一点事情，但还没到我对自己期望的程度。 2012年，又是一年，尽管看起来这一年对于很多公司来说将并不好过。我的愿望很简单：希望能做更多落地的事情，做一些实实在在的事情，2011年我给了自己一个希冀，希望2012年年底，能对自己有一个满意的答复。希望年底再总结的时候，能说今年干得还不错。 最后，新的一年，希望大家都有一个好的起点。 落后的，只要踏踏实实，很快会赶上； 领先的，想的也要更多一点。 总之，我们又有一整年的时间可以挥霍，只这一点便已让人感到无比幸福！]]></description>
			<content:encoded><![CDATA[<p>如果要用一个词来形容2011的话，我想，就用“<span style="color: #ff0000;"><strong>激动</strong></span>”好了。</p>
<p>这一年我从学校出来，由一个胆胆颤颤的学生，正式转变成一名工作者，经济上的收入虽然不高，但勉强可以做到自收自支，更多的是，开始把自己的能力发挥出来，能够做一些<span style="color: #ff0000;"><strong>真正产生影响的东西</strong></span>，<span style="color: #ff0000;"><strong>这是我一直以来都想做的事情</strong></span>。</p>
<p>这一年，很多在书上看到的技术产品，总算可以真的亲手去用，去体验，去借助我的代码直接触摸那些曾经让我觉得无比高耸的高峰。</p>
<p>这一年，我终于成为团队的一员，从小作坊的独立开发，慢慢适应协同开发、合作，不得不说，和一群相投的人一起做事，真的很开心！<span id="more-884"></span></p>
<p><span style="color: #ff6600;"><strong>Timeline</strong></span></p>
<p>3月份到了学校以后，心态比较焦虑，关于实习的事情，一方面自己很想有机会能出去看看，能真的去做一些事情，另一方面，又唯恐自己还达不到要求，做得不好。那段时间一直上自习看书，和考研的兄弟们一起窝在考研自习室钻研。后来正巧朋友飘零发推说招实习生，周末匆匆忙忙准备了一份简历就投过去了，周一下午我还在自习室看分布式，大概四点二十意外接到了面试的电话，全是技术问题，自己觉得答得比较一般了，有两个问题没答得比较顺畅。</p>
<p>晚上七点半的时候，面试官，也就是我现在的老大，打来电话，恭喜我通过面试，正式“邀请”我去淘宝实习（当时觉得，这么一家大公司，对一个学生能用“邀请”这个词，真是很难得）。当时和女朋友坐在公交车上，她知道我当时有多开心。</p>
<p>然后就是完成在学校的任务了，师兄列了一些需要掌握的知识点出来，我就自己看书去学，这个过程中，眼界开阔了许多，也因为更有方向感，能够定下心来深入地去学习一番，这段时间，可以说自己的成长是飞速的。</p>
<p>7月15号终于完成了学校的实训项目，过了一周我就到了公司。</p>
<p>其实刚来的时候，真的很不习惯，首先就是自己会的东西，和现有的事情没法很好地接轨，简单地说，会的用不上，要做的都不会。那段时间真的很纠结，因为自己很想尽快上手去做一些有价值的事情，自己也主动找老大聊了几次，慢慢找到方向感。</p>
<p>后来就是做的第一个项目了，在这个项目中，真正接触到了很多以前只是在文字上见过的东西，例如JNI（为此我还复习了一点C/C++）、HSF、Tair、Hadoop等等，像分布式RPC、分布式缓存、并发一系列的东西，能够说自己用以前自己的理解去用起来，实现了一个高效的异步生产消费模型，充分发挥了机器的能力，把CPU、IO利用率都提升了很多，也成为后来处理海量数据的一个基础设施。</p>
<p>后面越来越熟悉现有的架构、框架，慢慢有些观念也改变了，比如业务和技术，比如对产品的理解，越来越希望能把自己的产品做得更好，思考什么才是更好，用什么样的技术去做。<span style="color: #ff0000;"><strong>做的很多东西也上线了，真正的对用户产生了影响。</strong></span></p>
<p>2011年一共写了13篇Blog，<span style="color: #ff0000;"><strong>数量不多，但质量都还可以，明年希望能产出更多分享的东西。</strong></span></p>
<p>2011年，成长良多，要感激很多人，身边的师兄们，网上的朋友们，也感谢2011年的自己，很快适应了新的环境，也总算做了一点事情，但还没到我对自己期望的程度。</p>
<p>2012年，又是一年，尽管看起来这一年对于很多公司来说将并不好过。我的愿望很简单：<span style="color: #ff0000;"><strong>希望能做更多落地的事情，做一些实实在在的事情</strong></span>，2011年我给了自己一个希冀，希望2012年年底，能对自己有一个满意的答复。希望年底再总结的时候，能说今年干得还不错。</p>
<p>最后，<span style="color: #ff0000;"><strong>新的一年，希望大家都有一个好的起点。</strong></span></p>
<p>落后的，只要踏踏实实，很快会赶上；</p>
<p>领先的，想的也要更多一点。</p>
<p>总之，我们又有一整年的时间可以挥霍，只这一点便已让人感到无比幸福！</p>
]]></content:encoded>
			<wfw:commentRss>http://blog.hesey.net/2011/12/review-my-2011.html/feed</wfw:commentRss>
		<slash:comments>4</slash:comments>
		</item>
		<item>
		<title>本机搭建Hadoop伪分布式模式</title>
		<link>http://blog.hesey.net/2011/12/build-pseudo-distributed-mode-on-localhost.html?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=build-pseudo-distributed-mode-on-localhost</link>
		<comments>http://blog.hesey.net/2011/12/build-pseudo-distributed-mode-on-localhost.html#comments</comments>
		<pubDate>Thu, 29 Dec 2011 12:34:40 +0000</pubDate>
		<dc:creator>Hesey</dc:creator>
				<category><![CDATA[分布式]]></category>
		<category><![CDATA[技术]]></category>

		<guid isPermaLink="false">http://blog.hesey.net/?p=878</guid>
		<description><![CDATA[Hadoop运行时有三种模式： 单机模式 伪分布式模式 完全分布式模式 前两种可以在单机运行，最后一种用于真实的集群环境，通常用在生产环境上。我们可以搭建本地的伪分布式模式来模拟分布式环境的执行。 步骤如下： 1、去http://www.apache.org/dyn/closer.cgi/hadoop/core/下载Hadoop 2、编辑conf/hadoop-env.sh，将 # export JAVA_HOME=/usr/lib/j2sdk1.5-sun 这行改为 export JAVA_HOME=/usr/local/jdk1.6.0_30 路径是JDK安装的路径，可以在Shell用which java查看，注意是JDK不是JRE噢~ 3、解压后，编辑conf目录下的文件： 给core-site.xml添加配置： &#60;property&#62; &#60;name&#62;fs.default.name&#60;/name&#62; &#60;value&#62;hdfs://127.0.0.1:9000/&#60;/value&#62; &#60;/property&#62; &#60;property&#62; &#60;name&#62;hadoop.tmp.dir&#60;/name&#62; &#60;!-- 配置工作的临时目录 --&#62; &#60;value&#62;/home/hesey/tmp/hadoop-hesey&#60;/value&#62; &#60;/property&#62; 给mapred-site.xml添加配置： &#60;property&#62; &#60;name&#62;mapred.job.tracker&#60;/name&#62; &#60;value&#62;127.0.0.1:9001&#60;/value&#62; &#60;/property&#62; 给hdfs-site.xml添加配置： &#60;property&#62; &#60;name&#62;dfs.replication&#60;/name&#62; &#60;value&#62;1&#60;/value&#62; &#60;/property&#62; 4、在Hadoop目录下执行： bin/hadoop namenode -format 格式化NameNode 5、在Hadoop目录下执行： bin/start-all.sh 启动所有组件 6、Shell下执行jps命令，可以看到： 3919 DataNode 4119 SecondaryNameNode 3740 NameNode 4427 [...]]]></description>
			<content:encoded><![CDATA[<p>Hadoop运行时有三种模式：</p>
<blockquote><p>单机模式</p>
<p>伪分布式模式</p>
<p>完全分布式模式</p></blockquote>
<p>前两种可以在单机运行，最后一种用于真实的集群环境，通常用在生产环境上。我们可以搭建本地的伪分布式模式来模拟分布式环境的执行。<span id="more-878"></span></p>
<p>步骤如下：</p>
<p>1、去<a href="http://www.apache.org/dyn/closer.cgi/hadoop/core/">http://www.apache.org/dyn/closer.cgi/hadoop/core/</a>下载Hadoop</p>
<p>2、编辑conf/hadoop-env.sh，将</p>
<pre class="brush:[bash]"># export JAVA_HOME=/usr/lib/j2sdk1.5-sun</pre>
<p>这行改为</p>
<pre class="brush:[bash]">export JAVA_HOME=/usr/local/jdk1.6.0_30</pre>
<p>路径是JDK安装的路径，可以在Shell用which java查看，注意是JDK不是JRE噢~</p>
<p>3、解压后，编辑conf目录下的文件：</p>
<p>给core-site.xml添加配置：</p>
<pre class="brush:[xml]">&lt;property&gt;
&lt;name&gt;fs.default.name&lt;/name&gt;
&lt;value&gt;hdfs://127.0.0.1:9000/&lt;/value&gt;
&lt;/property&gt;

&lt;property&gt;
&lt;name&gt;hadoop.tmp.dir&lt;/name&gt;
&lt;!-- 配置工作的临时目录 --&gt;
&lt;value&gt;/home/hesey/tmp/hadoop-hesey&lt;/value&gt;
&lt;/property&gt;</pre>
<p>给mapred-site.xml添加配置：</p>
<pre class="brush:[xml]">&lt;property&gt;
&lt;name&gt;mapred.job.tracker&lt;/name&gt;
&lt;value&gt;127.0.0.1:9001&lt;/value&gt;
&lt;/property&gt;</pre>
<p>给hdfs-site.xml添加配置：</p>
<pre class="brush:[xml]">&lt;property&gt;
&lt;name&gt;dfs.replication&lt;/name&gt;
&lt;value&gt;1&lt;/value&gt;
&lt;/property&gt;</pre>
<p>4、在Hadoop目录下执行：</p>
<pre class="brush:[bash]">bin/hadoop namenode -format</pre>
<p>格式化NameNode</p>
<p>5、在Hadoop目录下执行：</p>
<pre class="brush:[bash]">bin/start-all.sh</pre>
<p>启动所有组件</p>
<p>6、Shell下执行jps命令，可以看到：</p>
<p>3919 DataNode<br />
4119 SecondaryNameNode<br />
3740 NameNode<br />
4427 Jps<br />
4365 TaskTracker<br />
4187 JobTracker</p>
<p>这个时候就可以跑Job啦，如果有错误可以去logs目录下面看日志</p>
]]></content:encoded>
			<wfw:commentRss>http://blog.hesey.net/2011/12/build-pseudo-distributed-mode-on-localhost.html/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Redis容灾策略</title>
		<link>http://blog.hesey.net/2011/11/failover-of-redis.html?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=failover-of-redis</link>
		<comments>http://blog.hesey.net/2011/11/failover-of-redis.html#comments</comments>
		<pubDate>Wed, 09 Nov 2011 13:54:25 +0000</pubDate>
		<dc:creator>Hesey</dc:creator>
				<category><![CDATA[技术]]></category>
		<category><![CDATA[数据库]]></category>

		<guid isPermaLink="false">http://blog.hesey.net/?p=872</guid>
		<description><![CDATA[Redis利用内存发挥的高性能读写在很多场景下大有所为，但是Redis本身毕竟还是一个单机数据库，如果系统对其属于强依赖，那么还是必须做好必要的容灾，针对这个问题，有以下几种策略： 一、M/S切换 由于Redis是单机数据库，所以针对MySQL的一些容灾方案也能顺利适用，例如当Redis意外宕机，可以将请求马上切到备库，同时快速恢复数据。 二、AOF Redis有两种持久化的方式，分别是SnapShotting和Append-Only File，其原理和特性可以参考《对redis数据持久化的一些想法》一文，总得来说，快照对性能影响更小，也只会备份需要的数据，但两次快照中间的数据是没法保证一定会持久化的。 相比之下AOF的粒度更细，持久化效果更好，类似于MySQL的BinLog，缺点是会损失一部分性能，而且会记录不必要的日志，这一点当系统运行时间很长时会特别突出，也许恢复所有数据本来只要1个小时，却可能要花100甚至1000小时去搞。 三、读取数据源直接恢复 这个方案是和业务场景相关的，由于之前某个项目中Redis起到了存放索引的作用，所以利用MySQL的数据可以容易地重新建立Redis的所有内容，这里可以临时搞一个Trigger，不断读MySQL写Redis，利用MySQL的顺序读和Redis高TPS的特性，实践中，可以在几分钟内重建上千万的数据量。 目前Redis在功能上通常仍用于Cache，如果需要保证HA的持久化存储，请考虑具体场景，此外也可以考虑是否可以使用原生分布式的memcached或升级硬件（如SSD、Fusion-IO）增强原有DB的性能。]]></description>
			<content:encoded><![CDATA[<p>Redis利用内存发挥的高性能读写在很多场景下大有所为，但是Redis本身毕竟还是一个单机数据库，如果系统对其属于强依赖，那么还是必须做好必要的容灾，针对这个问题，有以下几种策略：</p>
<h1>一、M/S切换</h1>
<blockquote><p>由于Redis是单机数据库，所以针对MySQL的一些容灾方案也能顺利适用，例如当Redis意外宕机，可以将请求马上切到备库，同时快速恢复数据。</p></blockquote>
<h1>二、AOF</h1>
<blockquote><p>Redis有两种持久化的方式，分别是SnapShotting和Append-Only File，其原理和特性可以参考《<a href="http://www.yiihsia.com/2011/04/%E5%AF%B9redis%E6%95%B0%E6%8D%AE%E6%8C%81%E4%B9%85%E5%8C%96%E7%9A%84%E4%B8%80%E4%BA%9B%E6%83%B3%E6%B3%95/"><span style="color: #008000;"><strong>对redis数据持久化的一些想法</strong></span></a>》一文，总得来说，快照对性能影响更小，也只会备份需要的数据，但两次快照中间的数据是没法保证一定会持久化的。</p>
<p>相比之下AOF的粒度更细，持久化效果更好，类似于MySQL的BinLog，缺点是会损失一部分性能，而且会记录不必要的日志，这一点当系统运行时间很长时会特别突出，也许恢复所有数据本来只要1个小时，却可能要花100甚至1000小时去搞。</p></blockquote>
<h1>三、读取数据源直接恢复</h1>
<blockquote><p>这个方案是和业务场景相关的，由于之前某个项目中Redis起到了存放索引的作用，所以利用MySQL的数据可以容易地重新建立Redis的所有内容，这里可以临时搞一个Trigger，不断读MySQL写Redis，利用MySQL的顺序读和Redis高TPS的特性，实践中，可以在几分钟内重建上千万的数据量。</p></blockquote>
<p>目前Redis在功能上通常仍用于Cache，如果需要保证HA的持久化存储，请考虑具体场景，此外也可以考虑是否可以使用原生分布式的memcached或升级硬件（如SSD、Fusion-IO）增强原有DB的性能。</p>
]]></content:encoded>
			<wfw:commentRss>http://blog.hesey.net/2011/11/failover-of-redis.html/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>用AtomicStampedReference解决ABA问题</title>
		<link>http://blog.hesey.net/2011/09/resolve-aba-by-atomicstampedreference.html?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=resolve-aba-by-atomicstampedreference</link>
		<comments>http://blog.hesey.net/2011/09/resolve-aba-by-atomicstampedreference.html#comments</comments>
		<pubDate>Wed, 28 Sep 2011 02:12:42 +0000</pubDate>
		<dc:creator>Hesey</dc:creator>
				<category><![CDATA[Java]]></category>
		<category><![CDATA[技术]]></category>

		<guid isPermaLink="false">http://blog.hesey.net/?p=859</guid>
		<description><![CDATA[在运用CAS做Lock-Free操作中有一个经典的ABA问题： 线程1准备用CAS将变量的值由A替换为B，在此之前，线程2将变量的值由A替换为C，又由C替换为A，然后线程1执行CAS时发现变量的值仍然为A，所以CAS成功。但实际上这时的现场已经和最初不同了，尽管CAS成功，但可能存在潜藏的问题，例如下面的例子： 现有一个用单向链表实现的堆栈，栈顶为A，这时线程T1已经知道A.next为B，然后希望用CAS将栈顶替换为B： head.compareAndSet(A,B); 在T1执行上面这条指令之前，线程T2介入，将A、B出栈，再pushD、C、A，此时堆栈结构如下图，而对象B此时处于游离状态： 此时轮到线程T1执行CAS操作，检测发现栈顶仍为A，所以CAS成功，栈顶变为B，但实际上B.next为null，所以此时的情况变为： 其中堆栈中只有B一个元素，C和D组成的链表不再存在于堆栈中，平白无故就把C、D丢掉了。 以上就是由于ABA问题带来的隐患，各种乐观锁的实现中通常都会用版本戳version来对记录或对象标记，避免并发操作带来的问题，在Java中，AtomicStampedReference&#60;E&#62;也实现了这个作用，它通过包装[E,Integer]的元组来对对象标记版本戳stamp，从而避免ABA问题，例如下面的代码分别用AtomicInteger和AtomicStampedReference来对初始值为100的原子整型变量进行更新，AtomicInteger会成功执行CAS操作，而加上版本戳的AtomicStampedReference对于ABA问题会执行CAS失败： public class Test { private static AtomicInteger atomicInt = new AtomicInteger(100); private static AtomicStampedReference atomicStampedRef = new AtomicStampedReference(100, 0); public static void main(String[] args) throws InterruptedException { Thread intT1 = new Thread(new Runnable() { @Override public void run() { atomicInt.compareAndSet(100, 101); atomicInt.compareAndSet(101, 100); } }); Thread intT2 [...]]]></description>
			<content:encoded><![CDATA[<p>在运用CAS做Lock-Free操作中有一个经典的ABA问题：</p>
<p>线程1准备用CAS将变量的值由A替换为B，在此之前，线程2将变量的值由A替换为C，又由C替换为A，然后线程1执行CAS时发现变量的值仍然为A，所以CAS成功。但实际上这时的现场已经和最初不同了，尽管CAS成功，但可能存在潜藏的问题，例如下面的例子：</p>
<p><a href="http://blog.hesey.net/wp-content/uploads/2011/09/ABA-1.png"><img class="aligncenter size-full wp-image-860" title="ABA-1" src="http://blog.hesey.net/wp-content/uploads/2011/09/ABA-1.png" alt="" width="149" height="173" /></a></p>
<p><span id="more-859"></span>现有一个用单向链表实现的堆栈，栈顶为A，这时线程T1已经知道A.next为B，然后希望用CAS将栈顶替换为B：</p>
<p>head.compareAndSet(A,B);</p>
<p>在T1执行上面这条指令之前，线程T2介入，将A、B出栈，再pushD、C、A，此时堆栈结构如下图，而对象B此时处于游离状态：</p>
<p><a href="http://blog.hesey.net/wp-content/uploads/2011/09/ABA-2.png"><img class="aligncenter size-full wp-image-861" title="ABA-2" src="http://blog.hesey.net/wp-content/uploads/2011/09/ABA-2.png" alt="" width="149" height="256" /></a></p>
<p>此时轮到线程T1执行CAS操作，检测发现栈顶仍为A，所以CAS成功，栈顶变为B，但实际上B.next为null，所以此时的情况变为：</p>
<p><a href="http://blog.hesey.net/wp-content/uploads/2011/09/ABA-3.png"><img class="aligncenter size-full wp-image-862" title="ABA-3" src="http://blog.hesey.net/wp-content/uploads/2011/09/ABA-3.png" alt="" width="342" height="222" /></a></p>
<p>其中堆栈中只有B一个元素，C和D组成的链表不再存在于堆栈中，平白无故就把C、D丢掉了。</p>
<p>以上就是由于ABA问题带来的隐患，各种乐观锁的实现中通常都会用版本戳version来对记录或对象标记，避免并发操作带来的问题，在Java中，AtomicStampedReference&lt;E&gt;也实现了这个作用，它通过包装[E,Integer]的元组来对对象标记版本戳stamp，从而避免ABA问题，例如下面的代码分别用AtomicInteger和AtomicStampedReference来对初始值为100的原子整型变量进行更新，AtomicInteger会成功执行CAS操作，而加上版本戳的AtomicStampedReference对于ABA问题会执行CAS失败：</p>
<pre class="brush:[java]">public class Test {
    private static AtomicInteger atomicInt = new AtomicInteger(100);
    private static AtomicStampedReference atomicStampedRef = new AtomicStampedReference(100, 0);

    public static void main(String[] args) throws InterruptedException {
       Thread intT1 = new Thread(new Runnable() {
           @Override
           public void run() {
              atomicInt.compareAndSet(100, 101);
              atomicInt.compareAndSet(101, 100);
           }
       });

       Thread intT2 = new Thread(new Runnable() {
           @Override
           public void run() {
              try {
                  TimeUnit.SECONDS.sleep(1);
              } catch (InterruptedException e) {
              }
              boolean c3 = atomicInt.compareAndSet(100, 101);
              System.out.println(c3); // true
           }
       });

       intT1.start();
       intT2.start();
       intT1.join();
       intT2.join();

       Thread refT1 = new Thread(new Runnable() {
           @Override
           public void run()
              try {
                  TimeUnit.SECONDS.sleep(1);
              } catch (InterruptedException e) {
              }
              atomicStampedRef.compareAndSet(100, 101, atomicStampedRef.getStamp(), atomicStampedRef.getStamp() + 1);
              atomicStampedRef.compareAndSet(101, 100, atomicStampedRef.getStamp(), atomicStampedRef.getStamp() + 1);
           }
       });

       Thread refT2 = new Thread(new Runnable() {
           @Override
           public void run() {
              int stamp = atomicStampedRef.getStamp();
              try {
                  TimeUnit.SECONDS.sleep(2);
              } catch (InterruptedException e) {
              }
              boolean c3 = atomicStampedRef.compareAndSet(100, 101, stamp, stamp + 1);
              System.out.println(c3); // false
           }
       });

       refT1.start();
       refT2.start();
    }
}</pre>
]]></content:encoded>
			<wfw:commentRss>http://blog.hesey.net/2011/09/resolve-aba-by-atomicstampedreference.html/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>对象都是在堆上分配的吗？</title>
		<link>http://blog.hesey.net/2011/07/object-allocation-on-non-heap.html?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=object-allocation-on-non-heap</link>
		<comments>http://blog.hesey.net/2011/07/object-allocation-on-non-heap.html#comments</comments>
		<pubDate>Fri, 22 Jul 2011 02:34:03 +0000</pubDate>
		<dc:creator>Hesey</dc:creator>
				<category><![CDATA[Java]]></category>
		<category><![CDATA[技术]]></category>

		<guid isPermaLink="false">http://blog.hesey.net/?p=852</guid>
		<description><![CDATA[学习面向对象的过程中通常的说法是new出来的对象都是分配在堆上的，那么这个结论是不是如此绝对，有没有反例呢？ 在Java中，典型的对象不再堆上分配的情况有两种：TLAB和栈上分配。 一、为什么不在堆上分配 我们知道堆是由所有线程共享的，既然如此那它就是竞争资源，对于竞争资源，必须采取必要的同步，所以当使用new关键字在堆上分配对象时，是需要锁的。既然有锁，就必定存在锁带来的开销，而且由于是对整个堆加锁，相对而言锁的粒度还是比较大的，当对象频繁分配时，不免影响效率。 所以对于某些特殊情况，可以采取避免在堆上分配对象的办法，以提高对象创建和销毁的效率。 二、TLAB分配 JVM在内存新生代Eden Space中开辟了一小块区域，由线程私有，称作TLAB（Thread-local allocation buffer），默认设定为占用Eden Space的1%。在Java程序中很多对象都是小对象且用过即丢，它们不存在线程共享也适合被快速GC，所以对于小对象通常JVM会优先分配在TLAB上，并且TLAB上的分配由于是线程私有所以没有锁开销。因此在实践中分配多个小对象的效率通常比分配一个大对象的效率要高。 三、栈上分配 JVM在Server模式下的逃逸分析可以分析出某个对象是否永远只在某个方法、线程的范围内，并没有“逃逸”出这个范围，逃逸分析的一个结果就是对于某些未逃逸对象可以直接在栈上分配，由于该对象一定是局部的，所以栈上分配不会有问题。 四、对象非堆上分配的思想和启发 对象不在堆上分配主要的原因还是堆是共享的，在堆上分配有锁的开销。无论是TLAB还是栈都是线程私有的，私有即避免了竞争（当然也可能产生额外的问题例如可见性问题），这是典型的用空间换效率的做法。 在实践中，类似的做法还有很多，例如Hadoop中对于Map过程在节点的本地内存中处理，直到最后Reduce过程再合并数据。对于任务之间可以分解到不同线程、进程的情况，就可以采用类似的做法用空间换效率，对吞吐率的提升有很大帮助。]]></description>
			<content:encoded><![CDATA[<p>学习面向对象的过程中通常的说法是new出来的对象都是分配在堆上的，那么这个结论是不是如此绝对，有没有反例呢？</p>
<p>在Java中，典型的对象不再堆上分配的情况有两种：<span style="color: #ff0000;"><strong>TLAB</strong></span>和<span style="color: #ff0000;"><strong>栈上分配</strong></span>。<span id="more-852"></span></p>
<h1>一、为什么不在堆上分配</h1>
<blockquote><p>我们知道堆是由所有线程共享的，既然如此那它就是竞争资源，对于竞争资源，必须采取必要的同步，所以当使用new关键字在堆上分配对象时，是需要锁的。既然有锁，就必定存在锁带来的开销，而且由于是对整个堆加锁，相对而言锁的粒度还是比较大的，当对象频繁分配时，不免影响效率。</p>
<p>所以对于某些特殊情况，可以采取避免在堆上分配对象的办法，以提高对象创建和销毁的效率。</p></blockquote>
<h1>二、TLAB分配</h1>
<blockquote><p>JVM在内存新生代Eden Space中开辟了一小块区域，由线程私有，称作<a href="http://wikis.sun.com/display/MaxineVM/Threads#Threads-Threadlocalallocationbuffer%28TLAB%29" target="_blank"><span style="color: #008000;"><strong>TLAB</strong></span></a>（Thread-local allocation buffer），默认设定为占用Eden Space的1%。在Java程序中很多对象都是小对象且用过即丢，它们不存在线程共享也适合被快速GC，所以对于小对象通常JVM会优先分配在TLAB上，并且TLAB上的分配由于是线程私有所以没有锁开销。因此在实践中分配多个小对象的效率通常比分配一个大对象的效率要高。</p></blockquote>
<h1>三、栈上分配</h1>
<blockquote><p>JVM在Server模式下的逃逸分析可以分析出某个对象是否永远只在某个方法、线程的范围内，并没有“逃逸”出这个范围，逃逸分析的一个结果就是对于某些未逃逸对象可以直接在栈上分配，由于该对象一定是局部的，所以栈上分配不会有问题。</p></blockquote>
<h1>四、对象非堆上分配的思想和启发</h1>
<blockquote><p>对象不在堆上分配主要的原因还是堆是共享的，在堆上分配有锁的开销。无论是TLAB还是栈都是线程私有的，私有即避免了竞争（当然也可能产生额外的问题例如可见性问题），这是典型的用空间换效率的做法。</p>
<p>在实践中，类似的做法还有很多，例如Hadoop中对于Map过程在节点的本地内存中处理，直到最后Reduce过程再合并数据。对于任务之间可以分解到不同线程、进程的情况，就可以采用类似的做法用空间换效率，对吞吐率的提升有很大帮助。</p></blockquote>
]]></content:encoded>
			<wfw:commentRss>http://blog.hesey.net/2011/07/object-allocation-on-non-heap.html/feed</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>理解重排序</title>
		<link>http://blog.hesey.net/2011/07/reordering.html?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=reordering</link>
		<comments>http://blog.hesey.net/2011/07/reordering.html#comments</comments>
		<pubDate>Wed, 20 Jul 2011 06:28:05 +0000</pubDate>
		<dc:creator>Hesey</dc:creator>
				<category><![CDATA[Java]]></category>
		<category><![CDATA[并发]]></category>
		<category><![CDATA[技术]]></category>

		<guid isPermaLink="false">http://blog.hesey.net/?p=842</guid>
		<description><![CDATA[重排序通常是编译器或运行时环境为了优化程序性能而采取的对指令进行重新排序执行的一种手段。重排序分为两类：编译期重排序和运行期重排序，分别对应编译时和运行时环境。 在并发程序中，程序员会特别关注不同进程或线程之间的数据同步，特别是多个线程同时修改同一变量时，必须采取可靠的同步或其它措施保障数据被正确地修改，这里的一条重要原则是：不要假设指令执行的顺序，你无法预知不同线程之间的指令会以何种顺序执行。 但是在单线程程序中，通常我们容易假设指令是顺序执行的，否则可以想象程序会发生什么可怕的变化。理想的模型是：各种指令执行的顺序是唯一且有序的，这个顺序就是它们被编写在代码中的顺序，与处理器或其它因素无关，这种模型被称作顺序一致性模型，也是基于冯·诺依曼体系的模型。当然，这种假设本身是合理的，在实践中也鲜有异常发生，但事实上，没有哪个现代多处理器架构会采用这种模型，因为它是在是太低效了。而在编译优化和CPU流水线中，几乎都涉及到指令重排序。 一、编译期重排序 编译期重排序的典型就是通过调整指令顺序，在不改变程序语义的前提下，尽可能减少寄存器的读取、存储次数，充分复用寄存器的存储值。 假设第一条指令计算一个值赋给变量A并存放在寄存器中，第二条指令与A无关但需要占用寄存器（假设它将占用A所在的那个寄存器），第三条指令使用A的值且与第二条指令无关。那么如果按照顺序一致性模型，A在第一条指令执行过后被放入寄存器，在第二条指令执行时A不再存在，第三条指令执行时A重新被读入寄存器，而这个过程中，A的值没有发生变化。通常编译器都会交换第二和第三条指令的位置，这样第一条指令结束时A存在于寄存器中，接下来可以直接从寄存器中读取A的值，降低了重复读取的开销。 二、重排序对于流水线的意义 现代CPU几乎都采用流水线机制加快指令的处理速度，一般来说，一条指令需要若干个CPU时钟周期处理，而通过流水线并行执行，可以在同等的时钟周期内执行若干条指令，具体做法简单地说就是把指令分为不同的执行周期，例如读取、寻址、解析、执行等步骤，并放在不同的元件中处理，同时在执行单元EU中，功能单元被分为不同的元件，例如加法元件、乘法元件、加载元件、存储元件等，可以进一步实现不同的计算并行执行。 流水线架构决定了指令应该被并行执行，而不是在顺序化模型中所认为的那样。重排序有利于充分使用流水线，进而达到超标量的效果。 三、确保顺序性 尽管指令在执行时并不一定按照我们所编写的顺序执行，但毋庸置疑的是，在单线程环境下，指令执行的最终效果应当与其在顺序执行下的效果一致，否则这种优化便会失去意义。 通常无论是在编译期还是运行期进行的指令重排序，都会满足上面的原则。 四、Java存储模型中的重排序 在Java存储模型（Java Memory Model, JMM）中，重排序是十分重要的一节，特别是在并发编程中。JMM通过happens-before法则保证顺序执行语义，如果想要让执行操作B的线程观察到执行操作A的线程的结果，那么A和B就必须满足happens-before原则，否则，JVM可以对它们进行任意排序以提高程序性能。 volatile关键字可以保证变量的可见性，因为对volatile的操作都在Main Memory中，而Main Memory是被所有线程所共享的，这里的代价就是牺牲了性能，无法利用寄存器或Cache，因为它们都不是全局的，无法保证可见性，可能产生脏读。 volatile还有一个作用就是局部阻止重排序的发生，对volatile变量的操作指令都不会被重排序，因为如果重排序，又可能产生可见性问题。 在保证可见性方面，锁（包括显式锁、对象锁）以及对原子变量的读写都可以确保变量的可见性。但是实现方式略有不同，例如同步锁保证得到锁时从内存里重新读入数据刷新缓存，释放锁时将数据写回内存以保数据可见，而volatile变量干脆都是读写内存。]]></description>
			<content:encoded><![CDATA[<p>重排序通常是编译器或运行时环境为了优化程序性能而采取的对指令进行重新排序执行的一种手段。重排序分为两类：<span style="color: #ff0000;"><strong>编译期重排序</strong></span>和<span style="color: #ff0000;"><strong>运行期重排序</strong></span>，分别对应编译时和运行时环境。</p>
<p>在并发程序中，程序员会特别关注不同进程或线程之间的数据同步，特别是多个线程同时修改同一变量时，必须采取可靠的同步或其它措施保障数据被正确地修改，这里的一条重要原则是：<span style="color: #ff0000;"><strong>不要假设指令执行的顺序，你无法预知不同线程之间的指令会以何种顺序执行。</strong></span></p>
<p>但是在<span style="color: #ff0000;"><strong>单线程</strong></span>程序中，通常我们容易假设指令是<span style="color: #ff0000;"><strong>顺序执行</strong></span>的，否则可以想象程序会发生什么可怕的变化。理想的模型是：各种指令执行的顺序是唯一且有序的，这个顺序就是它们被编写在代码中的顺序，与处理器或其它因素无关，这种模型被称作<a href="http://en.wikipedia.org/wiki/Sequential_consistency" target="_blank"><span style="color: #008000;"><strong>顺序一致性模型</strong></span></a>，也是基于冯·诺依曼体系的模型。当然，这种假设本身是合理的，在实践中也鲜有异常发生，但事实上，<span style="color: #ff0000;"><strong>没有哪个现代多处理器架构会采用这种模型</strong></span>，因为它是在是太低效了。而在编译优化和CPU流水线中，几乎都涉及到指令重排序。<span id="more-842"></span></p>
<h1>一、编译期重排序</h1>
<blockquote><p>编译期重排序的典型就是通过调整指令顺序，在不改变程序语义的前提下，尽可能减少寄存器的读取、存储次数，充分复用寄存器的存储值。</p>
<p>假设第一条指令计算一个值赋给变量A并存放在寄存器中，第二条指令与A无关但需要占用寄存器（假设它将占用A所在的那个寄存器），第三条指令使用A的值且与第二条指令无关。那么如果按照顺序一致性模型，A在第一条指令执行过后被放入寄存器，在第二条指令执行时A不再存在，第三条指令执行时A重新被读入寄存器，而这个过程中，A的值没有发生变化。通常编译器都会交换第二和第三条指令的位置，这样第一条指令结束时A存在于寄存器中，接下来可以直接从寄存器中读取A的值，降低了重复读取的开销。</p></blockquote>
<h1>二、重排序对于流水线的意义</h1>
<blockquote><p>现代CPU几乎都采用流水线机制加快指令的处理速度，一般来说，一条指令需要若干个CPU时钟周期处理，而通过流水线并行执行，可以在同等的时钟周期内执行若干条指令，具体做法简单地说就是把指令分为不同的执行周期，例如读取、寻址、解析、执行等步骤，并放在不同的元件中处理，同时在执行单元EU中，功能单元被分为不同的元件，例如加法元件、乘法元件、加载元件、存储元件等，可以进一步实现不同的计算并行执行。</p>
<p>流水线架构决定了指令应该被并行执行，而不是在顺序化模型中所认为的那样。重排序有利于充分使用流水线，进而达到<a href="http://zh.wikipedia.org/wiki/%E8%B6%85%E7%B4%94%E9%87%8F" target="_blank"><span style="color: #008000;"><strong>超标量</strong></span></a>的效果。</p></blockquote>
<h1>三、确保顺序性</h1>
<blockquote><p>尽管指令在执行时并不一定按照我们所编写的顺序执行，但毋庸置疑的是，<span style="color: #ff0000;"><strong>在单线程环境下，指令执行的最终效果应当与其在顺序执行下的效果一致</strong></span>，否则这种优化便会失去意义。</p>
<p>通常无论是在编译期还是运行期进行的指令重排序，都会满足上面的原则。</p></blockquote>
<h1>四、Java存储模型中的重排序</h1>
<blockquote><p>在Java存储模型（Java Memory Model, JMM）中，重排序是十分重要的一节，特别是在并发编程中。JMM通过happens-before法则保证顺序执行语义，如果想要让执行操作B的线程观察到执行操作A的线程的结果，那么A和B就必须满足happens-before原则，否则，<span style="color: #ff0000;"><strong>JVM可以对它们进行任意排序以提高程序性能。</strong></span></p>
<p>volatile关键字可以<span style="color: #ff0000;"><strong>保证变量的可见性</strong></span>，因为对volatile的操作都在Main Memory中，而Main Memory是被所有线程所共享的，这里的代价就是牺牲了性能，无法利用寄存器或Cache，因为它们都不是全局的，无法保证可见性，可能产生脏读。</p>
<p>volatile还有一个作用就是<span style="color: #ff0000;"><strong>局部阻止重排序的发生</strong></span>，对volatile变量的操作指令都不会被重排序，因为如果重排序，又可能产生可见性问题。</p>
<p>在保证可见性方面，锁（包括显式锁、对象锁）以及对原子变量的读写都可以确保变量的可见性。但是实现方式略有不同，例如同步锁保证得到锁时从内存里重新读入数据刷新缓存，释放锁时将数据写回内存以保数据可见，而volatile变量干脆都是读写内存。</p></blockquote>
]]></content:encoded>
			<wfw:commentRss>http://blog.hesey.net/2011/07/reordering.html/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Yahoo! S4：分布式流计算平台</title>
		<link>http://blog.hesey.net/2011/04/yahoo-s4-distributed-stream-computing-platform.html?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=yahoo-s4-distributed-stream-computing-platform</link>
		<comments>http://blog.hesey.net/2011/04/yahoo-s4-distributed-stream-computing-platform.html#comments</comments>
		<pubDate>Tue, 19 Apr 2011 11:28:53 +0000</pubDate>
		<dc:creator>Hesey</dc:creator>
				<category><![CDATA[分布式]]></category>
		<category><![CDATA[技术]]></category>

		<guid isPermaLink="false">http://blog.hesey.net/?p=816</guid>
		<description><![CDATA[一、概述 S4（Simple Scalable Streaming System）最初是Yahoo!为提高搜索广告有效点击率的问题而开发的一个平台，通过统计分析用户对广告的点击率，排除相关度低的广告，提升点击率。目前该项目刚启动不久，所以也可以理解为是他们提出的一个分布式流计算（Distributed Stream Computing）的模型。 S4的设计目标是： ·提供一种简单的编程接口来处理数据流 ·设计一个可以在普通硬件之上可扩展的高可用集群。 ·通过在每个处理节点使用本地内存，避免磁盘I/O瓶颈达到最小化延迟 ·使用一个去中心的，对等架构；所有节点提供相同的功能和职责。没有担负特殊责任的中心节点。这大大简化了部署和维护。 ·使用可插拔的架构，使设计尽可能的即通用又可定制化。 ·友好的设计理念，易于编程，具有灵活的弹性 系统基于如下假设： ·一旦一个节点失败，会failover到另一个standby节点，但是会丢失原节点的内存状态，这也就是为什么说S4是一个部分容错的系统。 ·节点不能动态增加和减少。 二、与MapReduce的区别 通常对于大规模分布式数据的处理会首先想到MapReduce，Yahoo!也维护了Hadoop项目，但是他们最终放弃了扩展Hadoop的想法，因为相比之下，流计算面对的场景和需求是完全不同的。 流计算强调的是数据流的形式和实时性。MapReduce系统主要解决的是对静态数据的批量处理，当MapReduce任务启动时，一般数据已经到位了（比如保存到了分布式文件系统上）。而流式计算系统在启动时，一般数据并没有完全到位，而是经由外部数据流源源不断地流入，并且不像批处理系统重视的是总数据处理的吞吐，而是对数据处理的低延迟，希望进入的数据越快处理越好，这里的思想是数据的价值随着时间的递增而递减，所以数据越快被处理，结果就越有价值，这也是实时处理的价值所在。 MapReduce采用的是一种比较静态的模型，如果用它做数据流的处理，首先需要将数据流缓存并分块，然后放入集群运算。如果数据块分得太小，可以获得一定的低延迟以保障实时性，但是包括集群启动之类的额外开销将会占很大比重；如果数据块分得太大，将无法满足低延迟的需求，达不到实时性的要求。 流计算的数据本身就是数据流，不需要数据准备的时间，有数据即可流入同时计算，同时解决了数据准备和延迟的两个问题，所以流计算是一种有别于MapReduce的分布式计算模型。同时高速数据流的速率也不是MapReduce的设计可以承担的，流计算系统没有批处理的概念，而是自启动开始，每时每刻都在处理实时流入的数据。这也是为什么S4的开发小组一直在强调S4 is not Real-Time MapReduce。 三、实现逻辑 1、Event 数据流是事件（Event）的序列流。每个Event是一个（K,A）元素，通过EventType来标示其类型。K、A分别表示这种类型的 Event的若干个key和若干个attribute。key和attribute都是tuple-valued，即key=value这种元组值。例如： EventType(EV): TradeEvent KEY: product=”T-shirt” type=”buy it now” Attribute: customerId=”1234” time=”2011-4-19 01:21:31” 2、Processing Elements Processing Elements(PE)是S4中的基本计算单元，一个PE通过下面四个组件来表示： functionality：由实现PE的Java类和相关配置来定义。 types of events：处理的Event Type。 key：关心哪种key。 key的值：关心（匹配）的key值是多少。 每个PE只负责处理自己所关心的event type,并且只处理自己所对应的key值的event，也就是说，只有当event type, [...]]]></description>
			<content:encoded><![CDATA[<h1>一、概述</h1>
<blockquote><p>S4（Simple Scalable Streaming System）最初是Yahoo!为提高搜索广告有效点击率的问题而开发的一个平台，通过统计分析用户对广告的点击率，排除相关度低的广告，提升点击率。目前该项目刚启动不久，所以也可以理解为是他们提出的一个分布式流计算（Distributed Stream Computing）的模型。<span id="more-816"></span></p>
<p>S4的设计目标是：</p>
<blockquote><p>·提供一种简单的编程接口来处理数据流</p>
<p>·设计一个可以在普通硬件之上可扩展的高可用集群。</p>
<p>·通过在每个处理节点使用本地内存，避免磁盘I/O瓶颈达到最小化延迟</p>
<p>·使用一个去中心的，对等架构；所有节点提供相同的功能和职责。没有担负特殊责任的中心节点。这大大简化了部署和维护。</p>
<p>·使用可插拔的架构，使设计尽可能的即通用又可定制化。</p>
<p>·友好的设计理念，易于编程，具有灵活的弹性</p></blockquote>
<p>系统基于如下假设：</p>
<blockquote><p>·一旦一个节点失败，会failover到另一个standby节点，但是会丢失原节点的内存状态，这也就是为什么说S4是一个部分容错的系统。</p>
<p>·节点不能动态增加和减少。</p></blockquote>
</blockquote>
<h1>二、与MapReduce的区别</h1>
<blockquote><p>通常对于大规模分布式数据的处理会首先想到MapReduce，Yahoo!也维护了Hadoop项目，但是他们最终放弃了扩展Hadoop的想法，因为相比之下，流计算面对的场景和需求是完全不同的。</p>
<p>流计算强调的是数据流的形式和实时性。MapReduce系统主要解决的是对静态数据的批量处理，当MapReduce任务启动时，一般数据已经到位了（比如保存到了分布式文件系统上）。而流式计算系统在启动时，一般数据并没有完全到位，而是经由外部数据流源源不断地流入，并且不像批处理系统重视的是总数据处理的吞吐，而是对数据处理的低延迟，希望进入的数据越快处理越好，这里的思想是数据的价值随着时间的递增而递减，所以数据越快被处理，结果就越有价值，这也是实时处理的价值所在。</p>
<p>MapReduce采用的是一种比较静态的模型，如果用它做数据流的处理，首先需要将数据流缓存并分块，然后放入集群运算。如果数据块分得太小，可以获得一定的低延迟以保障实时性，但是包括集群启动之类的额外开销将会占很大比重；如果数据块分得太大，将无法满足低延迟的需求，达不到实时性的要求。</p>
<p>流计算的数据本身就是数据流，不需要数据准备的时间，有数据即可流入同时计算，同时解决了数据准备和延迟的两个问题，所以流计算是一种有别于MapReduce的分布式计算模型。同时高速数据流的速率也不是MapReduce的设计可以承担的，流计算系统没有批处理的概念，而是自启动开始，每时每刻都在处理实时流入的数据。这也是为什么S4的开发小组一直在强调S4 is not Real-Time MapReduce。</p></blockquote>
<h1>三、实现逻辑</h1>
<blockquote>
<h3>1、Event</h3>
<blockquote><p>数据流是事件（Event）的序列流。每个Event是一个（K,A）元素，通过EventType来标示其类型。K、A分别表示这种类型的 Event的若干个key和若干个attribute。key和attribute都是tuple-valued，即key=value这种元组值。例如：</p>
<p>EventType(EV): TradeEvent</p>
<p>KEY: product=”T-shirt” type=”buy it now”</p>
<p>Attribute: customerId=”1234” time=”2011-4-19 01:21:31”</p></blockquote>
<h3>2、Processing Elements</h3>
<blockquote><p><a href="http://blog.hesey.net/wp-content/uploads/2011/04/PE.png"></a><a href="http://blog.hesey.net/wp-content/uploads/2011/04/PE.png"><img class="aligncenter size-full wp-image-827" title="PE" src="http://blog.hesey.net/wp-content/uploads/2011/04/PE.png" alt="" width="550" height="236" /></a><br />
Processing Elements(PE)是S4中的基本计算单元，一个PE通过下面四个组件来表示：</p>
<blockquote><p>functionality：由实现PE的Java类和相关配置来定义。</p>
<p>types of events：处理的Event Type。</p>
<p>key：关心哪种key。</p>
<p>key的值：关心（匹配）的key值是多少。</p></blockquote>
<p>每个PE只负责处理自己所关心的event type,并且只处理自己所对应的key值的event，也就是说，只有当event type, key, key的值都匹配时，才会交由该PE进行计算处理。</p>
<p>这里要注意的是，如果有匹配的PE，则交由该PE处理，如果没有，则会创建一个新的PE。所以一个PN中的PE可能有许多个，这就需要在前期对事件的key及其取值范围进行很好的划分，否则可能因为过多的PE导致系统效率降低，同时也应该定期对使用率较低的PE进行清除，但是由于数据存放在节点内存中，所以清除前应该对有必要的数据进行持久化处理，否则会永久性丢失。后期应该会在PE上添加优先级等属性，可以提升清除PE工作的准确率。</p>
<p>有一种PE没有属性key和attribute，它们可以处理指定event type的所有事件，通常这些事件是原始数据，这类PE一般放置在S4集群的输入层，在这里原始事件会被赋予一个key以便于分发给后面的PE处理。PE处理后可能输出一个或多个event，输出频率在配置文件中定义，可以配置指定时间间隔输出一次或是指定事件数发生后输出一次，特例是可以配置为每次事件触发都输出一次。</p>
<p>PE是直接面向业务方的组件，由业务方定义PE中对事件的处理和处理以后的输出，剩下的事情全部交由平台去做。</p></blockquote>
<h3>3、Processing Node</h3>
<blockquote><p>Processing Node(PN)对应着集群中的每一个逻辑结点，主要工作是监听事件，当事件到达时调用适合的PE处理事件，如果PE有输出，则还需和通讯层合作进行事件的分发和输出。需要注意的是，集群中所有的PN都是对等的，使得集群的部署和维护相对简单，没有中心节点。其结构如下图：</p>
<p><a href="http://blog.hesey.net/wp-content/uploads/2011/04/PN.png"><img class="aligncenter size-full wp-image-824" title="PN" src="http://blog.hesey.net/wp-content/uploads/2011/04/PN.png" alt="" width="346" height="350" /></a>Event Listener负责监听事件并转交给PE容器(Processing Element Container, PEC)，由PEC交由合适的PE处理业务逻辑。配置文件中还会配置PE原型（PE prototype），定义其功能、event type、key。PN对其监听到的每一个唯一属性值会触发一次操作，如果当前没有合适的PE处理该事件，则会根据该PE原型创建新的对应了该唯一属性值的PE来对事件进行处理。</p>
<p>配置文件中定义了S4集群所关心的key的集合，这里会通过对事件的event type, key, key的值计算哈希函数，以路由到指定的PN，此外单个事件可能会被路由到多个PN中，这里应该是为将来做负载均衡做的准备，也可以作为广播事件的途径。</p>
<p>PEC中有若干个PE分别对应不同的情况，这里的思想即是Actor模型。PE处理完逻辑后根据其定义的输出方法可以输出事件，事件交由Dispatcher与通讯层(Communication Layer)进行交互并由Emiter输出至逻辑节点。</p>
<p>结果就是，所有包含特性属性值的事件在理论上都能通过哈希函数到达相应的PN，并被路由到PN内的PE上处理。</p></blockquote>
<h3>4、Communication Layer</h3>
<blockquote><p>通讯层提供集群管理、故障恢复（failover）到备用节点、逻辑节点到物理节点的映射。当监测到硬件故障时，会自动更新映射。通讯层隐藏的映射使得PN发送消息时只需要关心逻辑节点而不用关心物理节点。</p>
<p>通讯层采用插件式的架构来选择网络协议。它会对事件本身采用不同的协议发送，例如对于控制消息，会采用可靠方式（例如TCP）发送，而为了优化集群的吞吐率，可能会采用不可靠的方式（例如UDP）发送数据消息，这在S4中是可以容忍的。</p></blockquote>
<h3>5、Configuration Management System</h3>
<blockquote><p>配置管理系统主要用于对集群的操作，包括为S4任务创建和销毁集群，分配新的物理节点到S4任务集群中，空闲的集群可以作为冷备。这里的一致性保证交由ZooKeeper来做。</p></blockquote>
<p>S4系统的数据流整体上看起来类似下图：<br />
<a href="http://blog.hesey.net/wp-content/uploads/2011/04/Cluster.png"><img class="aligncenter size-full wp-image-822" title="Cluster" src="http://blog.hesey.net/wp-content/uploads/2011/04/Cluster.png" alt="" width="590" height="265" /></a></p></blockquote>
<h1>四、编程模型</h1>
<blockquote><p>面向业务方的PE编程十分简单，只需要编写处理事件的processEvent函数和输出的output函数即可，PE本身可以定义一些变量进行本地的数据记录，同时S4本身提供了一些持久化的辅助，主要类都在io.ts4.persist包下面。值得注意的是在API文档中可以看到，持久化的类通常都包含一个Clock对象，可以在初始化时定义持久时间，到时间以后会被抛弃。</p></blockquote>
<h1>五、性能</h1>
<blockquote><p>开发小组运用S4系统分别进行了在线实验和离线实验（压力测试）。</p>
<p>在线实验集群有16台服务器，每台4个32位CPU，2GB内存。每天大约有25万用户发起共100万次搜索，实验两周内观察到的峰值是每秒1600个事件。实验结果表明S4增加了3%的广告点击，主要通过快速检测低质量广告并把它们过滤出去达成。</p>
<p>离线实验集群有8台服务器，每台服务器4个64位CPU，16GB内存。集群中跑了16个PN，每台2个，事件由300万服务和点击组成。这次实验主要用于评估系统在远高于期望事件流量下的性能。</p>
<p>压力测试结果如下：</p>
<p><a href="http://blog.hesey.net/wp-content/uploads/2011/04/Result.png"><img class="aligncenter size-full wp-image-825" title="Result" src="http://blog.hesey.net/wp-content/uploads/2011/04/Result.png" alt="" width="590" height="264" /></a>可见当事件流越来越大时，S4系统的错误越来越多，当数据流速达到9.7Mbps时，开始出现错误，这主要是由于S4系统无法及时处理过多的事件而导致了丢失数据的情况。</p></blockquote>
<h1>六、系统分析</h1>
<blockquote><p>S4是面向流式数据和实时处理的，所以针对实时性较高的业务，可以很好地对数据做出高效的分析处理，而且系统一旦上线，很少需要人工干预，源源不断的数据流会被自动路由并分析。对于海量数据，它和MapReduce类似都可以应对，但它能比后者更快地处理数据。</p>
<p>S4目前的缺点在于它的数据传输可靠性还不够，可能丢失数据，同时由于数据存放在内存中，一旦节点出现故障，就会丢失该节点的所有数据，这一点可以通过定期持久化来弥补（但是真的有必要吗？）。同时我认为这和它面向的场景也有关系，实时数据分析通常都是针对一些非常离散、细小的数据，从统计的角度来看，损失掉一部分数据对最后的统计结果并没有很大影响，而这部分牺牲却可以换来吞吐率的大幅提升。所以就目前来看，S4还是更适合对那些不一定非得对每条数据都仔细分析的场景，只求最后一个统计的结果来对业务做出相应的预计和调整。此外S4系统要求输入的是事件流，这就涉及到事件的生成，所以在数据流入S4以前，必须有能将数据转化为事件的系统进行中间处理。</p>
<p>从集群的扩展性来看，理论上可以通过增加节点应对更大的数据流，但是目前还无法在S4工作时动态增加或减少节点。所以对节点进行调整时很可能必须停下当前的工作，做不到无缝调整。而且由于S4由ZooKeeper进行集群管理，所以当集群增加到一定规模时，ZooKeeper的管理能力也有待考验。此外，仍然是因为S4无法保证数据100%的可靠传输，所以集群规模增长时，数据错误也会增长得很快。目前没有相关资料显示S4集群的规模究竟可以做到多大，但是相信未来随着数据传输可靠性的提升，会发挥很可观的作用。</p>
<p>在业务耦合度方面，S4完全隔离了平台和业务逻辑，业务方只需要编写PE逻辑即可，这一点类似于MapReduce中只需编写map和reduce函数，业务和平台的耦合度是非常低的。</p></blockquote>
<h1>七、前景</h1>
<blockquote><p>从目前来看，流式计算的前景非常广阔，目前已经有一些基于流的服务出台。例如Twitter的Streaming API，可以实时推送（Push）消息，Google也利用该API实现了实时搜索。未来无论从数据的产生还是流动上都会有数量级的飞跃，如何对海量数据做出及时、实时的处理，相信不论是实际业务还是数据挖掘领域都会十分关注。也可以独立开发自己的或者基于S4的流式计算平台，以契合自身的业务需求。新的信息不仅对数据分析有用，也能给予用户更好的体验（Yahoo!做S4的初衷就是提升用广告的用户体验以增加点击率）。相信S4会成为继Hadoop之后的又一分布式计算领域的干将。未来的数据将不再是一个一个包的概念，而成为一道道数据流源源不断地流入系统，产出效益。</p></blockquote>
<p>附：Yahoo! S4论文：<a href="http://labs.yahoo.com/files/KDCloud%202010%20S4.pdf"><span style="color: #008000;"><strong>http://labs.yahoo.com/files/KDCloud%202010%20S4.pdf</strong></span></a></p>
]]></content:encoded>
			<wfw:commentRss>http://blog.hesey.net/2011/04/yahoo-s4-distributed-stream-computing-platform.html/feed</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>QCon2011大会4月8日PPT学习总结</title>
		<link>http://blog.hesey.net/2011/04/study-notes-of-qcon2011-4-8.html?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=study-notes-of-qcon2011-4-8</link>
		<comments>http://blog.hesey.net/2011/04/study-notes-of-qcon2011-4-8.html#comments</comments>
		<pubDate>Sat, 09 Apr 2011 13:55:42 +0000</pubDate>
		<dc:creator>Hesey</dc:creator>
				<category><![CDATA[技术]]></category>
		<category><![CDATA[数据库]]></category>
		<category><![CDATA[架构]]></category>

		<guid isPermaLink="false">http://blog.hesey.net/?p=767</guid>
		<description><![CDATA[最近正在召开QCon北京2011大会，趁此机会学习了一部分QCon大会的PPT，大会的PPT可以在QCon大会官网下载：http://www.qconbeijing.com/schedule.html 主题：淘宝商品库优化实践 讲师：淘宝核心系统数据库组 余锋（褚霸） 在技术方案的着眼点上，讲师主要强调了：通过高性能硬件支持平台，充分利用Cache（这一点在之后的演讲中多次强调），充分考虑容灾性。同时讲师表示要大胆借助开源产品提高系统性能。 在I/O能力的分配上，提高读能力的方式是零散读，提高IOPS，对于提高写能力采用了集中写，提高吞吐量。 在Cache分配上，商品库除了采用MySQL的内部存储、虚拟内存以外，还利用了Facebook的Flashcache技术（在文件系统和设备驱动之间新增一次缓存层，实现对热门数据的缓存，一般用SSD作为介质的缓存，通过将传统硬盘上的热门数据缓存到SSD上，然后利用SSD优秀的读性能，来加速系统）。这个方法较之内存缓存，没有内存快，但是可用空间可以比内存大很多。此外还利用了RAID卡内部Cache。 对于系统调优的指导思想，重视理论结合实际，不靠主观猜测而是按实际效果做决定。“内存为王”，用加大内存提升系统性能（其实也是间接加大了缓存）。随机数据和顺序数据分离，随机数据采用Direct IO（优点是CPU占用率小，适用于缓存命中率较低的文件），顺序数据通过缓存读。在保证安全的前提下，一定要充分利用系统各级的Cache。 在业务上优化掉复杂查询（类似天涯论坛的做法）。对于InnoDB，分配尽可能大的Buffer Pool，日志数据分开存储，减少锁对多核CPU的影响（减小锁的开销），适时清除InnoDB产生的日志Cache。 主题：MongoDB应用实践 讲师：视觉中国 潘凡 随着系统规模的增大，面临的新挑战主要有：数据量增大，服务器硬件有限，属性更新频繁以及由于内存不足导致的Memcached缓存命中率降低等问题。 为此视觉中国期望通过采用持久化的分布式Key-Value存储来解决问题。经过系统迁移，最终视觉中国大胆地完全抛弃了MySQL，采用纯NoSQL的解决方案，这主要是基于网站的以下特点：可以没有事务，对数据一致性的要求并不高，很少使用多表查询（类似的，在天涯论坛的演讲中讲师讲到论坛早期通过数据冗余的方式成功消除了多表查询的开销）。在这一点上，我认为还是必须基于对自身系统充分的理解和分析上，根据自身情况进行架构调整，例如对于安全性高的操作，事务就不能丢，对数据一致性的要求也比较高。 选择MongoDB的原因在于：已经有大站成功的经验，互联网上分享MongoDB的信息逐渐丰富，10gen团队在邮件列表提供了快速及时的技术支持，同时NoSQL也是大势所趋。视觉中国采用的是手动编译的MongoDB，在达到自定义需求的同时保持系统的高度稳定。 迁移后的CPU使用率降低了很多，同时系统对内存需求较大，内存必须要放得下索引，实践经验是4G以上为佳。 同时新数据库面临很多新的问题，例如频繁的DB操作导致碎片增加，删除数据后磁盘空间得不到释放，解决方案是定期重建索引，压缩数据块。这一点和MySQL的OPTIMIZE TABLE很像。实践中也发现MongoDB存在以下致命缺陷：单机可靠性低、部署时没有考虑机房断电的特殊情况（为此网站曾损失10个小时的数据）。解决方案：减少刷新磁盘时间，提高数据持久化的频率，为此引入了额外的I/O开销，但是这种影响在可接受范围内，并且保证了数据的安全，同时采用了批量写入的方式。 讲师还强调了对于开源产品，绝不使用开发版本而是用稳定版本。由于网站带宽不足，他们利用了国外相对便宜的服务器，但为此带来了很高的延时，对此就要通过压缩数据等方式减少网络数据的传输量。 主题：构建高性能的微博系统 讲师：新浪网 杨卫华 讲师首先强调了对于开源产品的使用原则：用好一款开源产品的前提条件是深入了解它的定位。的确是这样，在不了解产品的情况下就盲目上阵容易造成后期开发和维护的困难，甚至是产品根本不适合实际场景。这一点我认为不仅要广泛参考官方文档以及网上的评测，还必须自己对产品做足够多的测试才行。 新浪微博采用了Redis作为NoSQL的解决方案，适合的主要场景有：需要高速读写访问，无高准确性需求，能容忍短期不可用，有List/Set数据结构需求（附加功能）。对于数据结构，讲师认为“小即是美”，尽可能采用简洁的数据结构，而在网络传输过程中也要努力减少数据量，例如新浪微博采用了不传输字段名称而是以编号代替之的方式，以及使用Google的Protocol Buffers作为数据交换格式，相比JSON非常小并且序列化/反序列化很高效，目前支持Java/C++/Python三种语言。 新浪并没有像视觉中国那样完全放弃MySQL，而是利用了MySQL的可靠性加上NoSQL的特性作为补充。在系统中大量使用了异步传输和内存缓存来提升效率和吞吐率。对于接口的设计，以下方面是最重要的：安全、易用、性能。 主题：为速度而生——百姓网如何优化网速 讲师：百姓网 潘晓良 在HTTP响应中去除了ETag标签，减少了整整一半的流量，同时并不影响客户端的缓存。在前段优化中，主要参考了《高性能网站建设指南》和《高性能网站建设进阶指南》两本书，之后讲的很多优化手段都在这两本书中可以找到，顺便一提，这两本书堪称是网站工程师的必读书（不仅是前端工程师），读过以后的收获非常大，直叹前端工程师真是不容易啊。 讲师强调了要充分利用CDN来加速网络，静态CDN对静态内容优化非常有效，而动态CDN对动态内容的优化有限，特别是，选个好机房才是根本，这一点是其它人没有涉及到的，的确选一个好机房能够降低公司人员工作的难度，也节省了人力开支。 同时百姓网非常强调实时监测，联想到百姓网10人的技术团队，深切感受到“灵活”的特点，针对动态情况作及时反应，让系统在微调中时刻处于最佳状态，这或许才是调优最精髓之处。]]></description>
			<content:encoded><![CDATA[<p>最近正在召开QCon北京2011大会，趁此机会学习了一部分QCon大会的PPT，大会的PPT可以在QCon大会官网下载：<a href="http://www.qconbeijing.com/schedule.html"><span style="color: #008000;"><strong>http://www.qconbeijing.com/schedule.html</strong></span></a></p>
<h1>主题：淘宝商品库优化实践</h1>
<h1>讲师：淘宝核心系统数据库组 余锋（褚霸）</h1>
<blockquote><p>在技术方案的着眼点上，讲师主要强调了：<span style="color: #ff0000;"><strong>通过高性能硬件支持平台，充分利用Cache（这一点在之后的演讲中多次强调），充分考虑容灾性</strong></span>。同时讲师表示要<span style="color: #ff0000;"><strong>大胆借助开源产品提高系统性能</strong></span>。</p>
<p>在I/O能力的分配上，提高读能力的方式是<span style="color: #ff0000;"><strong>零散读，提高IOPS</strong></span>，对于提高写能力采用了<span style="color: #ff0000;"><strong>集中写，提高吞吐量</strong></span>。</p>
<p>在Cache分配上，商品库除了采用MySQL的内部存储、虚拟内存以外，还利用了Facebook的<a href="https://github.com/facebook/flashcache/"><span style="color: #008000;"><strong>Flashcache</strong></span></a>技术（在文件系统和设备驱动之间新增一次缓存层，实现对热门数据的缓存，一般用SSD作为介质的缓存，通过将传统硬盘上的热门数据缓存到SSD上，然后利用SSD优秀的读性能，来加速系统）。这个方法较之内存缓存，没有内存快，但是可用空间可以比内存大很多。此外还利用了RAID卡内部Cache。</p>
<p>对于系统调优的指导思想，<span style="color: #ff0000;"><strong>重视理论结合实际，不靠主观猜测而是按实际效果做决定</strong></span>。“内存为王”，用加大内存提升系统性能（其实也是间接加大了缓存）。随机数据和顺序数据分离，随机数据采用Direct IO（优点是CPU占用率小，适用于缓存命中率较低的文件），顺序数据通过缓存读。<span style="color: #ff0000;"><strong>在保证安全的前提下，一定要充分利用系统各级的Cache</strong></span>。</p>
<p>在业务上优化掉复杂查询（类似天涯论坛的做法）。对于InnoDB，分配尽可能大的Buffer Pool，日志数据分开存储，减少锁对多核CPU的影响（减小锁的开销），适时清除InnoDB产生的日志Cache。<span id="more-767"></span></p></blockquote>
<h1>主题：MongoDB应用实践</h1>
<h1>讲师：视觉中国 潘凡</h1>
<blockquote><p>随着系统规模的增大，面临的新挑战主要有：<span style="color: #ff0000;"><strong>数据量增大，服务器硬件有限，属性更新频繁以及由于内存不足导致的Memcached缓存命中率降低</strong></span>等问题。</p>
<p>为此视觉中国期望通过采用持久化的分布式Key-Value存储来解决问题。经过系统迁移，最终视觉中国大胆地完全抛弃了MySQL，采用纯NoSQL的解决方案，这主要是基于网站的以下特点：可以没有事务，对数据一致性的要求并不高，很少使用多表查询（类似的，在天涯论坛的演讲中讲师讲到论坛早期通过数据冗余的方式成功消除了多表查询的开销）。在这一点上，<span style="color: #ff0000;"><strong>我认为还是必须基于对自身系统充分的理解和分析上，根据自身情况进行架构调整</strong></span>，例如对于安全性高的操作，事务就不能丢，对数据一致性的要求也比较高。</p>
<p>选择MongoDB的原因在于：已经有大站成功的经验，互联网上分享MongoDB的信息逐渐丰富，10gen团队在邮件列表提供了快速及时的技术支持，同时NoSQL也是大势所趋。视觉中国采用的是手动编译的MongoDB，在达到自定义需求的同时保持系统的高度稳定。<img title="更多..." src="http://blog.hesey.net/wp-includes/js/tinymce/plugins/wordpress/img/trans.gif" alt="" /></p>
<p>迁移后的CPU使用率降低了很多，同时系统对内存需求较大，内存必须要放得下索引，实践经验是4G以上为佳。</p>
<p>同时新数据库面临很多新的问题，例如频繁的DB操作导致碎片增加，删除数据后磁盘空间得不到释放，解决方案是定期重建索引，压缩数据块。这一点和MySQL的OPTIMIZE   TABLE很像。实践中也发现MongoDB存在以下致命缺陷：单机可靠性低、部署时没有考虑机房断电的特殊情况（为此网站曾损失10个小时的数据）。解决方案：减少刷新磁盘时间，提高数据持久化的频率，为此引入了额外的I/O开销，但是这种影响在可接受范围内，并且保证了数据的安全，同时采用了批量写入的方式。</p>
<p>讲师还强调了对于开源产品，绝不使用开发版本而是用稳定版本。由于网站带宽不足，他们利用了国外相对便宜的服务器，但为此带来了很高的延时，对此就要通过压缩数据等方式减少网络数据的传输量。</p></blockquote>
<h1>主题：构建高性能的微博系统</h1>
<h1>讲师：新浪网 杨卫华</h1>
<blockquote><p>讲师首先强调了对于开源产品的使用原则：<span style="color: #ff0000;"><strong>用好一款开源产品的前提条件是深入了解它的定位</strong></span>。的确是这样，在不了解产品的情况下就盲目上阵容易造成后期开发和维护的困难，甚至是产品根本不适合实际场景。这一点我认为不仅要广泛参考官方文档以及网上的评测，还必须自己对产品做足够多的测试才行。</p>
<p>新浪微博采用了Redis作为NoSQL的解决方案，适合的主要场景有：需要高速读写访问，无高准确性需求，能容忍短期不可用，有List/Set数据结构需求（附加功能）。对于数据结构，讲师认为“小即是美”，尽可能采用简洁的数据结构，而在网络传输过程中也要努力减少数据量，例如新浪微博采用了不传输字段名称而是以编号代替之的方式，以及使用Google的<a href="http://code.google.com/p/protobuf/"><span style="color: #008000;"><strong>Protocol Buffers</strong></span></a>作为数据交换格式，相比JSON非常小并且序列化/反序列化很高效，目前支持Java/C++/Python三种语言。</p>
<p>新浪并没有像视觉中国那样完全放弃MySQL，而是利用了MySQL的可靠性加上NoSQL的特性作为补充。在系统中大量使用了异步传输和内存缓存来提升效率和吞吐率。对于接口的设计，以下方面是最重要的：安全、易用、性能。</p></blockquote>
<h1>主题：为速度而生——百姓网如何优化网速</h1>
<h1>讲师：百姓网 潘晓良</h1>
<blockquote><p>在HTTP响应中去除了ETag标签，减少了整整一半的流量，同时并不影响客户端的缓存。在前段优化中，主要参考了《<a href="http://book.douban.com/subject/3132277/"><span style="color: #008000;"><strong>高性能网站建设指南</strong></span></a>》和《<a href="http://book.douban.com/subject/4719162/"><span style="color: #008000;"><strong>高性能网站建设进阶指南</strong></span></a>》两本书，之后讲的很多优化手段都在这两本书中可以找到，顺便一提，这两本书堪称是网站工程师的必读书（不仅是前端工程师），读过以后的收获非常大，直叹前端工程师真是不容易啊。</p>
<p>讲师强调了要充分利用CDN来加速网络，静态CDN对静态内容优化非常有效，而动态CDN对动态内容的优化有限，特别是，<span style="color: #ff0000;"><strong>选个好机房才是根本</strong></span>，这一点是其它人没有涉及到的，的确选一个好机房能够降低公司人员工作的难度，也节省了人力开支。</p>
<p>同时百姓网非常强调实时监测，联想到百姓网10人的技术团队，深切感受到“灵活”的特点，<span style="color: #ff0000;"><strong>针对动态情况作及时反应，让系统在微调中时刻处于最佳状态，这或许才是调优最精髓之处</strong></span>。</p></blockquote>
]]></content:encoded>
			<wfw:commentRss>http://blog.hesey.net/2011/04/study-notes-of-qcon2011-4-8.html/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>NoSQL简介</title>
		<link>http://blog.hesey.net/2011/04/introduction-to-nosql.html?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=introduction-to-nosql</link>
		<comments>http://blog.hesey.net/2011/04/introduction-to-nosql.html#comments</comments>
		<pubDate>Sat, 09 Apr 2011 13:35:00 +0000</pubDate>
		<dc:creator>Hesey</dc:creator>
				<category><![CDATA[技术]]></category>
		<category><![CDATA[数据库]]></category>

		<guid isPermaLink="false">http://blog.hesey.net/?p=761</guid>
		<description><![CDATA[传统关系型数据库面临很多问题。 1、需要很高的实时插入性能：在高并发读写环境下，每秒上万次读请求勉强可以支撑，但是在每秒上万次写请求之下，硬盘I/O将无法承受。 2、需要海量数据存储能力的同时还需要非常快的查询检索速度：在一个表中存储数以亿计的记录后，使用SQL查询的效率是极为低下的。 3、需要将数据存储无缝扩展到整个集群：数据库的横向扩展比较困难，往往需要停机维护和数据迁移，不能像Web服务器那样简单地通过增加服务器数量来减轻负载。 针对高并发读写、海量存储以及可扩展性三个方向，有不同的NoSQL解决方案，这里对每个方向分别简要介绍了Redis、MongoDB、Cassandra三款NoSQL数据库。 高并发读写性能 Redis是基于Key-Value类型的内存数据库，数据都存放在内存中，定期通过异步同步将数据flush到硬盘上，内存操作避免了较慢的硬盘I/O，测试数据每秒可以处理10万个读写请求，是目前最快的Key-Value数据库之一。 Redis还支持存储List、Set结构并原生支持对它们的一些操作。 Redis最大的缺点是依赖物理内存，不适合存储大量数据。针对这个问题，Redis 2.0开发了虚拟内存的特性，将不经常使用的数据存放到硬盘上。 Redis没有采用操作系统的虚拟内存，主要是考虑到页中可能包含多个对象，而想写入硬盘的只有那些不常用的，以页为单位进行交换很难把握，另外Redis自己的虚拟内存可以在写入硬盘时去掉对象一些不必要的属性，官方说法是对象可以比内存中的大小小10倍。 海量存储 MongoDB是目前比较流行的面向文档的数据库，数据库的交互采用和JSON语法非常类似的BSON格式。 MongoDB支持存放比较复杂的对象类型，查询语法和面向对象语言的很像，也支持索引功能。官方文档的测试中当数据量达到50GB以上时，访问速度是MySQL的10倍以上，大约每秒可以处理0.5万~1.5万次的读写请求。 MongoDB自带GridFS支持分布式的海量存储。目前很多站点都采用或者准备往MongoDB迁移。 可扩展性 Cassandra是Facebook开源的产品，包括Facebook和Twitter都在用。它是基于分布式的一个数据库。写的时候写请求从客户端到达一个随机节点，先写日志再将写内容复制到若干个节点，保证当某些节点损坏时仍然可以获取数据。读请求到达随机节点后会被路由到某个节点上获取数据。 高扩展性体现在需要扩展数据库时增加节点就可以。Cassandra的读写性能并不是最优秀的，但是分布式的可扩展性弥补了这个缺陷。]]></description>
			<content:encoded><![CDATA[<p>传统关系型数据库面临很多问题。</p>
<blockquote><p>1、需要很高的实时插入性能：在高并发读写环境下，每秒上万次读请求勉强可以支撑，但是在每秒上万次写请求之下，硬盘I/O将无法承受。</p>
<p>2、需要海量数据存储能力的同时还需要非常快的查询检索速度：在一个表中存储数以亿计的记录后，使用SQL查询的效率是极为低下的。</p>
<p>3、需要将数据存储无缝扩展到整个集群：数据库的横向扩展比较困难，往往需要停机维护和数据迁移，不能像Web服务器那样简单地通过增加服务器数量来减轻负载。</p></blockquote>
<p>针对<span style="color: #ff0000;"><strong>高并发读写</strong></span>、<span style="color: #ff0000;"><strong>海量存储</strong></span>以及<span style="color: #ff0000;"><strong>可扩展性</strong></span>三个方向，有不同的NoSQL解决方案，这里对每个方向分别简要介绍了Redis、MongoDB、Cassandra三款NoSQL数据库。<span id="more-761"></span></p>
<h3><span style="color: #ff0000;">高并发读写性能</span></h3>
<blockquote><p>Redis是基于Key-Value类型的内存数据库，数据都存放在内存中，定期通过异步同步将数据flush到硬盘上，内存操作避免了较慢的硬盘I/O，测试数据每秒可以处理10万个读写请求，是目前最快的Key-Value数据库之一。</p>
<p>Redis还支持存储List、Set结构并原生支持对它们的一些操作。</p>
<p>Redis最大的缺点是依赖物理内存，不适合存储大量数据。针对这个问题，Redis 2.0开发了虚拟内存的特性，将不经常使用的数据存放到硬盘上。</p>
<p>Redis没有采用操作系统的虚拟内存，主要是考虑到页中可能包含多个对象，而想写入硬盘的只有那些不常用的，以页为单位进行交换很难把握，另外Redis自己的虚拟内存可以在写入硬盘时去掉对象一些不必要的属性，官方说法是对象可以比内存中的大小小10倍。</p></blockquote>
<h3><span style="color: #ff0000;">海量存储</span></h3>
<blockquote><p>MongoDB是目前比较流行的面向文档的数据库，数据库的交互采用和JSON语法非常类似的BSON格式。</p>
<p>MongoDB支持存放比较复杂的对象类型，查询语法和面向对象语言的很像，也支持索引功能。官方文档的测试中当数据量达到50GB以上时，访问速度是MySQL的10倍以上，大约每秒可以处理0.5万~1.5万次的读写请求。</p>
<p>MongoDB自带GridFS支持分布式的海量存储。目前很多站点都采用或者准备往MongoDB迁移。</p></blockquote>
<h3><span style="color: #ff0000;">可扩展性</span></h3>
<blockquote><p>Cassandra是Facebook开源的产品，包括Facebook和Twitter都在用。它是基于分布式的一个数据库。写的时候写请求从客户端到达一个随机节点，先写日志再将写内容复制到若干个节点，保证当某些节点损坏时仍然可以获取数据。读请求到达随机节点后会被路由到某个节点上获取数据。</p>
<p>高扩展性体现在需要扩展数据库时增加节点就可以。Cassandra的读写性能并不是最优秀的，但是分布式的可扩展性弥补了这个缺陷。</p></blockquote>
]]></content:encoded>
			<wfw:commentRss>http://blog.hesey.net/2011/04/introduction-to-nosql.html/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>高性能可伸缩系统构建的简要思想</title>
		<link>http://blog.hesey.net/2011/04/summary-of-construction-of-high-performance-scalable-system.html?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=summary-of-construction-of-high-performance-scalable-system</link>
		<comments>http://blog.hesey.net/2011/04/summary-of-construction-of-high-performance-scalable-system.html#comments</comments>
		<pubDate>Wed, 06 Apr 2011 05:24:26 +0000</pubDate>
		<dc:creator>Hesey</dc:creator>
				<category><![CDATA[分布式]]></category>
		<category><![CDATA[并发]]></category>
		<category><![CDATA[技术]]></category>
		<category><![CDATA[架构]]></category>

		<guid isPermaLink="false">http://blog.hesey.net/?p=733</guid>
		<description><![CDATA[为了支持高并发访问和海量数据的场景，在搭建应用时需要努力构建可伸缩的系统，这样在后期系统遇到瓶颈时可以简单地通过垂直伸缩或水平伸缩扩展系统。本文整理借鉴了林昊老师对构建可伸缩系统的文章。 一、垂直伸缩 垂直伸缩指通过增加或升级单台机器的硬件来获得对高并发访问和大数据量的更好支持。 1、支持高并发访问 可以增加CPU和内存，同时需要对系统进行一定调整，尽可能使得软件性能随硬件性能线性增长。 增加CPU时，需要进行如下调整： 首先需要减小锁的开销。当Java程序中线程比较多时，线程的上下文切换开销会增大，对锁的竞争也会变得更激烈，所以通常意义上线程并不是越多越好。当系统因为锁的竞争而导致CPU利用率较低时，可以采取以下策略减少锁带来的开销： a、使用Java API中的并发类库 可以采用java.util.concurrent等包下面的并发类，通常它们已经经过了充分的优化，能有效地支持高并发环境下的操作，并发类中大量采用了非阻塞算法，有些利用了CAS实现无锁。这里有一个小提示：使用并发哈希表时应优先采用ConcurrentHashMap而不是Hashtable，前者通过分解锁的方法使得效率更高。 b、用CAS代替锁 和第一点中提到的一样，CAS可以减小锁的开销，但是CAS本身是基于轮询的操作，实际使用中反而可能增加开销，这一点需要实验来测试。 c、减小锁的粒度 尽可能缩小锁定的范围，可以从两个方面入手。第一是只锁定所需的对象，少用synchronized(this)。第二是尽可能缩小锁定的方法块，缩小临界区大小，避免将不必要的操作也归入临界区。 d、拆分锁 将普通的对象锁、互斥锁按照场景拆分为读写锁或像ConcurrentHashMap一样拆分为若干把锁。 e、利用写时复制 对于读操作次数远远大于写操作的场景，可以在读操作时不加锁，写操作时利用写时复制来完成，但是内存占用会相应上升。 其次，系统中的线程数最好不是固定的，而是按CPU数来计算，这样当CPU增加时，相应的系统会自动增加线程提高并发率。 最后，如果对系统的CPU使用率还不满意，应当考虑分解一些单线程任务，改为多线程并发执行，以提高效率。 增加内存时，需要进行如下调整： 首先类似于线程数按CPU数计算，将Cache大小按内存大小计算，扩展内存后，Cache可以自动增长大小。 其次，可以分配更大的JVM堆内存给虚拟机，能减少OOM发生的几率。 2、支持海量数据 主要是通过拆分数据库和拆分表来实现，可以提升读写速度，但是分表也会增加开发系统的难度，可能存在跨表甚至跨数据库的操作。 二、水平伸缩 1、支持高并发访问 主要通过分散用户访问来实现，可以部署多个缓存服务器接受请求，这个过程中需要注意的问题是服务器之间的同步，通常有以下几种策略： a.广播 一台缓存服务器的数据更改时，通过广播的方式告诉其它服务器，其他服务器也进行相应的更改，这里会存在一定的延时。 b.分布式缓存 每台服务器只缓存一部分数据，通过Hash算法来实现请求的路由，Hash过程中必须对用户和节点进行至少两次Hash，以避免当集群中的机器增减后发生Hash失效的情况。 c.JVM集群堆共享 将若干台服务器的JVM实现堆共享，这种方式的问题是对堆的锁定，本来一个JVM的堆就存在同步，集群的堆共享会造成更激烈的锁竞争。 2、支持海量数据 采用分布式文件系统，例如Google的GFS、开源的HDFS、淘宝的TFS等支持海量文件的管理。随着NoSQL的逐渐普及，可以采用像10gen的MongoDB、淘宝的Tair等来支持分布式海量存储。 对于数据库，可以采用读写分离，MySQL、Oracle等数据库都支持读写分离的机制，这里需要处理好Master/Slave数据库同步的问题。MySQL支持对称复制和非对称复制，前者把Master的所有数据复制到Slave，后者相当于把Master的数据分割给若干个Slave。前者冗余度高但是容灾性比较好，后者冗余度低但一旦出现故障就没有备用数据库可用。 对于异构数据库，需要一个消息中间件转发写请求给被读的数据库。同时当采用多个数据库时存在分阶段提交和分布式事务的问题。 3、计算能力的增强 主要采用MapReduce算法将任务分布到集群中进行计算，可以基于开源的Hadoop进行系统构建。]]></description>
			<content:encoded><![CDATA[<p>为了支持高并发访问和海量数据的场景，在搭建应用时需要努力构建可伸缩的系统，这样在后期系统遇到瓶颈时可以简单地通过垂直伸缩或水平伸缩扩展系统。本文整理借鉴了林昊老师对构建可伸缩系统的文章。</p>
<h1>一、垂直伸缩</h1>
<blockquote><p>垂直伸缩指通过增加或升级单台机器的硬件来获得对高并发访问和大数据量的更好支持。</p>
<h3><span style="color: #ff0000;">1、支持高并发访问</span></h3>
<blockquote><p>可以增加CPU和内存，同时需要对系统进行一定调整，尽可能使得软件性能随硬件性能线性增长。</p>
<p><span style="color: #ff0000;"><strong>增加CPU</strong></span>时，需要进行如下调整：</p>
<p>首先需要减小锁的开销。当Java程序中线程比较多时，线程的上下文切换开销会增大，对锁的竞争也会变得更激烈，所以通常意义上线程并不是越多越好。当系统因为锁的竞争而导致CPU利用率较低时，可以采取以下策略减少锁带来的开销：</p>
<p><span style="color: #ff6600;"><strong>a、使用Java API中的并发类库</strong></span></p>
<blockquote><p>可以采用java.util.concurrent等包下面的并发类，通常它们已经经过了充分的优化，能有效地支持高并发环境下的操作，并发类中大量采用了非阻塞算法，有些利用了CAS实现无锁。这里有一个小提示：使用并发哈希表时应优先采用ConcurrentHashMap而不是Hashtable，前者通过分解锁的方法使得效率更高。<span id="more-733"></span></p></blockquote>
<p><span style="color: #ff6600;"><strong>b、用CAS代替锁</strong></span></p>
<blockquote><p>和第一点中提到的一样，CAS可以减小锁的开销，但是CAS本身是基于轮询的操作，实际使用中反而可能增加开销，这一点需要实验来测试。</p></blockquote>
<p><span style="color: #ff6600;"><strong>c、减小锁的粒度</strong></span></p>
<blockquote><p>尽可能缩小锁定的范围，可以从两个方面入手。第一是只锁定所需的对象，少用synchronized(this)。第二是尽可能缩小锁定的方法块，缩小临界区大小，避免将不必要的操作也归入临界区。</p></blockquote>
<p><span style="color: #ff6600;"><strong>d、拆分锁</strong></span></p>
<blockquote><p>将普通的对象锁、互斥锁按照场景拆分为读写锁或像ConcurrentHashMap一样拆分为若干把锁。</p></blockquote>
<p><span style="color: #ff6600;"><strong>e、利用写时复制</strong></span></p>
<blockquote><p>对于读操作次数远远大于写操作的场景，可以在读操作时不加锁，写操作时利用写时复制来完成，但是内存占用会相应上升。</p></blockquote>
<p>其次，系统中的线程数最好不是固定的，而是按CPU数来计算，这样当CPU增加时，相应的系统会自动增加线程提高并发率。</p>
<p>最后，如果对系统的CPU使用率还不满意，应当考虑分解一些单线程任务，改为多线程并发执行，以提高效率。</p>
<p><span style="color: #ff0000;"><strong>增加内存</strong></span>时，需要进行如下调整：</p>
<p>首先类似于线程数按CPU数计算，将Cache大小按内存大小计算，扩展内存后，Cache可以自动增长大小。</p>
<p>其次，可以分配更大的JVM堆内存给虚拟机，能减少OOM发生的几率。</p></blockquote>
<h3><span style="color: #ff0000;">2、支持海量数据</span></h3>
<blockquote><p>主要是通过拆分数据库和拆分表来实现，可以提升读写速度，但是分表也会增加开发系统的难度，可能存在跨表甚至跨数据库的操作。</p></blockquote>
</blockquote>
<h1>二、水平伸缩</h1>
<blockquote>
<h3><span style="color: #ff0000;">1、支持高并发访问</span></h3>
<blockquote><p>主要通过分散用户访问来实现，可以部署多个缓存服务器接受请求，这个过程中需要注意的问题是服务器之间的同步，通常有以下几种策略：</p>
<p><span style="color: #ff6600;"><strong>a.广播</strong></span></p>
<blockquote><p>一台缓存服务器的数据更改时，通过广播的方式告诉其它服务器，其他服务器也进行相应的更改，这里会存在一定的延时。</p></blockquote>
<p><span style="color: #ff6600;"><strong>b.分布式缓存</strong></span></p>
<blockquote><p>每台服务器只缓存一部分数据，通过Hash算法来实现请求的路由，Hash过程中必须对用户和节点进行至少两次Hash，以避免当集群中的机器增减后发生Hash失效的情况。</p></blockquote>
<p><span style="color: #ff6600;"><strong>c.JVM集群堆共享</strong></span></p>
<blockquote><p>将若干台服务器的JVM实现堆共享，这种方式的问题是对堆的锁定，本来一个JVM的堆就存在同步，集群的堆共享会造成更激烈的锁竞争。</p></blockquote>
</blockquote>
<h3><span style="color: #ff0000;">2、支持海量数据</span></h3>
<blockquote><p>采用分布式文件系统，例如Google的GFS、开源的HDFS、淘宝的TFS等支持海量文件的管理。随着NoSQL的逐渐普及，可以采用像10gen的MongoDB、淘宝的Tair等来支持分布式海量存储。</p>
<p>对于数据库，可以采用读写分离，MySQL、Oracle等数据库都支持读写分离的机制，这里需要处理好Master/Slave数据库同步的问题。MySQL支持对称复制和非对称复制，前者把Master的所有数据复制到Slave，后者相当于把Master的数据分割给若干个Slave。前者冗余度高但是容灾性比较好，后者冗余度低但一旦出现故障就没有备用数据库可用。</p>
<p>对于异构数据库，需要一个消息中间件转发写请求给被读的数据库。同时当采用多个数据库时存在分阶段提交和分布式事务的问题。</p></blockquote>
<h3><span style="color: #ff0000;">3、计算能力的增强</span></h3>
<blockquote><p>主要采用MapReduce算法将任务分布到集群中进行计算，可以基于开源的Hadoop进行系统构建。</p></blockquote>
</blockquote>
]]></content:encoded>
			<wfw:commentRss>http://blog.hesey.net/2011/04/summary-of-construction-of-high-performance-scalable-system.html/feed</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
	</channel>
</rss>

