<?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"
	>

<channel>
	<title>超群.com的博客</title>
	<atom:link href="http://chaoqun.17348.com/feed/" rel="self" type="application/rss+xml" />
	<link>http://chaoqun.17348.com</link>
	<description></description>
	<pubDate>Tue, 06 Jan 2009 09:43:22 +0000</pubDate>
	<generator>http://wordpress.org/?v=2.6.1</generator>
	<language>en</language>
			<item>
		<title>slope one算法在Netflix Prize上的表现</title>
		<link>http://chaoqun.17348.com/2009/01/slope-one-on-netflix-prize/</link>
		<comments>http://chaoqun.17348.com/2009/01/slope-one-on-netflix-prize/#comments</comments>
		<pubDate>Tue, 06 Jan 2009 09:40:58 +0000</pubDate>
		<dc:creator>超群.com</dc:creator>
		
		<category><![CDATA[Data Mining]]></category>

		<category><![CDATA[netflix prize]]></category>

		<category><![CDATA[Python]]></category>

		<category><![CDATA[slope one]]></category>

		<guid isPermaLink="false">http://chaoqun.17348.com/?p=125</guid>
		<description><![CDATA[本博客所有原创文章采用知识共享署名-非商业性使用-相同方式共享，转载请保留链接http://chaoqun.17348.com/2009/01/slope-one-on-netflix-prize/
前段时间做了个slope one算法在Netflix Pirze上的测试，关于slope one算法可以参考我之前写的文章:Slope one:简单高效的推荐算法，Netflix Prize是DVD在线租赁商Netflix于2006年10月2日发起一项竞赛：任何组织或个人只要能够提交比它现有电影推荐系统Cinematch效果好10%的新方法，就可以获得100万美元的奖金。竞赛最多持续到2011年10月2日。同时，NetflixPrize还提供每年5万美元的年度进步奖。为什么是五万美元的年度进步奖呢？因为100万美元大奖存银行，每年的利息是5万美元，看来老外是挺有意思的。
Netflix Pirze采用RMSE作为评测的标准，RMSE中文的意思是均方根误差，计算的公式： 

其中Ot是原始数据，Ft是预测数据，n是样本数。
目前最好的成绩是有BellKor in BigChaos创造的0.8598（2009.01.05数据），比官方数据提高9.63％，这样可以推算出官方的数据应该是0.9492左右，BellKor in BigChaos也是2008年度进步奖的获得者，他们更新数据的频率很高，如不出意外，最后的大奖应该是他们的。
目前的方向大多是SVD奇异值分解，可以参见BellKor in BigChaos他们的文章http://www.netflixprize.com/community/viewtopic.php?id=1193，这里有一个开源的项目nprize，也是一个SVD处理Netflix Prize的模型，大部分代码是Python的（国外很多用Python做数据挖掘和自然语言处理的案例），成绩到了0.9046，折算比官方数据要高4.70％，如果你想拿SVD热热手，可以看看这个开源项目，过些日子我会写一些SVD应用在推荐系统方面的文章，着急的可以阅读吴军的文章数学之美 系列十八 － 矩阵运算和文本处理中的分类问题。
由于对slope one算法比较熟悉，决定看看slope one算法在netflix pirze上的表现如何，netflix prize训练数据太大，大概有1亿多条的打分记录，OpenSlopeOne在运行效率方面不是很好，OpenSlopeOne处理几百万上千万的打分数据还是非常不错的，更多的话需要更多的机器水平扩展，如果有好的Mysql DBA可以尝试优化一下mysql server的参数，估计效果会很好，当然也需要有好的机器配合，我这里是用Python写的一个程序，多进程＋多线程＋手工Map-Reduce，需要源代码的话可以找我拿。
跑出来的结果不是很好，我的得分是0.9829，比官方数据差0.0337，大概差3.55％，分析原因可能是用户打分的数据普遍偏高，人们大多给自己喜欢的电影打分，对于不喜欢的数据打分就少很多了，下面是打分数据的分布情况：

mysql&#62; select rating,count(*) as times from nf_log group by rating;
+--------+----------+
&#124; rating &#124; times    &#124;
+--------+----------+
&#124;      1 &#124;  4617904 &#124;
&#124;      2 &#124; 10131945 &#124;
&#124;      3 &#124; 28810978 &#124;
&#124;      4 &#124; 33750581 &#124;
&#124;      5 &#124; 23167830 [...]]]></description>
			<content:encoded><![CDATA[<blockquote><p>本博客所有原创文章采用<a href="http://creativecommons.org/licenses/by-nc-sa/2.5/cn/" target="_blank"><span style="color: #356aa0;">知识共享署名-非商业性使用-相同方式共享</span></a>，转载请保留链接<a href="http://chaoqun.17348.com/2009/01/slope-one-on-netflix-prize/">http://chaoqun.17348.com/2009/01/slope-one-on-netflix-prize/</a></p></blockquote>
<p>前段时间做了个slope one算法在<a href="http://www.netflixprize.com/" target="_blank">Netflix Pirze</a>上的测试，关于slope one算法可以参考我之前写的文章:<a href="http://chaoqun.17348.com/2008/09/slope_one/" target="_blank">Slope one:简单高效的推荐算法</a>，Netflix Prize是DVD在线租赁商<a href="https://www.netflix.com" target="_blank">Netflix</a>于2006年10月2日发起一项竞赛：任何组织或个人只要能够提交比它现有电影推荐系统Cinematch效果好10%的新方法，就可以获得100万美元的奖金。竞赛最多持续到2011年10月2日。同时，NetflixPrize还提供每年5万美元的年度进步奖。为什么是五万美元的年度进步奖呢？因为100万美元大奖存银行，每年的利息是5万美元，看来老外是挺有意思的。</p>
<p>Netflix Pirze采用RMSE作为评测的标准，RMSE中文的意思是均方根误差，计算的公式： </p>
<p><img class="alignnone" src="http://farm2.static.flickr.com/1194/3171843795_1251bcff35.jpg" alt="" /></p>
<p>其中O<sup>t</sup>是原始数据，F<sup>t</sup>是预测数据，n是样本数。</p>
<p>目前最好的成绩是有<a href="http://www.commendo.at/prize08/team.html" target="_blank">BellKor in BigChaos</a>创造的0.8598（2009.01.05数据），比官方数据提高9.63％，这样可以推算出官方的数据应该是0.9492左右，BellKor in BigChaos也是2008年度进步奖的获得者，他们更新数据的频率很高，如不出意外，最后的大奖应该是他们的。</p>
<p>目前的方向大多是SVD奇异值分解，可以参见BellKor in BigChaos他们的文章<a href="http://www.netflixprize.com/community/viewtopic.php?id=1193" target="_blank">http://www.netflixprize.com/community/viewtopic.php?id=1193</a>，这里有一个开源的项目<a href="http://code.google.com/p/nprize/" target="_blank">nprize</a>，也是一个SVD处理Netflix Prize的模型，大部分代码是Python的（国外很多用Python做数据挖掘和自然语言处理的案例），成绩到了0.9046，折算比官方数据要高4.70％，如果你想拿SVD热热手，可以看看这个开源项目，过些日子我会写一些SVD应用在推荐系统方面的文章，着急的可以阅读吴军的文章<a href="http://www.googlechinablog.com/2007/01/blog-post.html" target="_blank">数学之美 系列十八 － 矩阵运算和文本处理中的分类问题</a>。</p>
<p>由于对slope one算法比较熟悉，决定看看slope one算法在netflix pirze上的表现如何，netflix prize训练数据太大，大概有1亿多条的打分记录，<a href="http://code.google.com/p/openslopeone/" target="_blank">OpenSlopeOne</a>在运行效率方面不是很好，OpenSlopeOne处理几百万上千万的打分数据还是非常不错的，更多的话需要更多的机器水平扩展，如果有好的Mysql DBA可以尝试优化一下mysql server的参数，估计效果会很好，当然也需要有好的机器配合，我这里是用Python写的一个程序，多进程＋多线程＋手工Map-Reduce，需要源代码的话可以找我拿。</p>
<p>跑出来的结果不是很好，我的得分是0.9829，比官方数据差0.0337，大概差3.55％，分析原因可能是用户打分的数据普遍偏高，人们大多给自己喜欢的电影打分，对于不喜欢的数据打分就少很多了，下面是打分数据的分布情况：</p>
<blockquote>
<pre>mysql&gt; select rating,count(*) as times from nf_log group by rating;
+--------+----------+
| rating | times    |
+--------+----------+
|      1 |  4617904 |
|      2 | 10131945 |
|      3 | 28810978 |
|      4 | 33750581 |
|      5 | 23167830 |
+--------+----------+</pre>
</blockquote>
<p>一目了然，用户对不喜欢的电影打分甚少，而且用户的平均打分达到了3.6043，所以最后算出来的打分值普遍偏高，RMSE的表现当然差了，<a href="http://www.daniel-lemire.com/fr/documents/publications/lemiremaclachlan_sdm05.pdf" target="_blank">Slope one算法</a>里面有个BI-POLAR SLOPE ONE来应对这种问题，有兴趣的可以尝试一下。</p>
<p>尽管得分情况不是很好，我用probe的数据测试看，slope one算法对用户喜欢的电影预测还是很好的，不擅长的是对用户不喜欢的电影的预测，对用户打分为1的预测普遍在3以上，拖累了它的表现。</p>
<p>我们一般要做的是对用户喜欢的东西进行推荐，slope one还是一个很不错的选择。</p>
]]></content:encoded>
			<wfw:commentRss>http://chaoqun.17348.com/2009/01/slope-one-on-netflix-prize/feed/</wfw:commentRss>
		</item>
		<item>
		<title>正走在路上</title>
		<link>http://chaoqun.17348.com/2008/12/my-2008/</link>
		<comments>http://chaoqun.17348.com/2008/12/my-2008/#comments</comments>
		<pubDate>Wed, 31 Dec 2008 07:18:33 +0000</pubDate>
		<dc:creator>超群.com</dc:creator>
		
		<category><![CDATA[默认分类]]></category>

		<guid isPermaLink="false">http://chaoqun.17348.com/?p=121</guid>
		<description><![CDATA[去年的这个时候我琢磨是不是要离职，事业单位，福利待遇都很好，离开了什么都要重新再来，思前想后，还是决定出来看看，毕竟在学校呆了18年了（小学5年＋初中3年＋高中3年＋大学4年＋学校出版社3年），在这个不大不小的年纪做这个决定是必要的，估计再过几年就不会动了，也动不了了。2008年2月27日，给出版社的同事写了一封致年轻人的信，然后3月份就到FH网去报到了，刚从学校出来的时候，可能那个经济环境还不错，拿到了些个offer，选择FH网是觉得是门户，公司大一些可能会正规一些，实际怎么样就不说了，一个项目下来，三个技术辞职，剩下的一个是测试，后来陆续在sina看到原来的同事来面试，想想出走还是明智的。6月份到sina报到，直至现在。
2008年成长最快的还是技术方面，PHP还算熟悉了，Javascript也凑合了，抽空做了做Python，已经离不开了，数据挖掘主要方向是推荐系统，也有些收获，过几天把netflix prize的分数晒晒。
与人交往方面还是没什么进步，基本上关系好的是我尊重的人和尊重我的人，所以你要成为我的好朋友，你需要有值得我尊重的东西，尤其是品质，或者尊重我，这样会友好很多。如果你不在二者之列，太对不起了，过去的一年我估计没少得罪你。
我出来之后有人问我出来感觉怎么样或者出来好不好，正好昨天看到一则小故事，人问佛：我不知道我现在的爱是真的还是假的，我该怎么办？佛说：有机会就去爱吧，不要管是劫还是缘，劫也是缘。所以有机会就试试吧，反正我的感觉很好。
2009年主要要做的事情：网络(socket)/桌面(gui)编程；一个精巧的PHP框架(雏形差不多了，和原生的PHP相比性能损耗大概在1.5ms左右)；数据挖掘(推荐系统差不多了，试试其他的领域，有可能在XX预测的方向)；多一些好朋友。
春天快要来了，因为人们都后知后觉，2009，大家都会是好样的。
]]></description>
			<content:encoded><![CDATA[<p>去年的这个时候我琢磨是不是要离职，事业单位，福利待遇都很好，离开了什么都要重新再来，思前想后，还是决定出来看看，毕竟在学校呆了18年了（小学5年＋初中3年＋高中3年＋大学4年＋学校出版社3年），在这个不大不小的年纪做这个决定是必要的，估计再过几年就不会动了，也动不了了。2008年2月27日，给出版社的同事写了一封<a href="http://chaoqun.17348.com/2008/08/a_letter_to_young_guys/" target="_blank">致年轻人的信</a>，然后3月份就到FH网去报到了，刚从学校出来的时候，可能那个经济环境还不错，拿到了些个offer，选择FH网是觉得是门户，公司大一些可能会正规一些，实际怎么样就不说了，一个项目下来，三个技术辞职，剩下的一个是测试，后来陆续在sina看到原来的同事来面试，想想出走还是明智的。6月份到sina报到，直至现在。</p>
<p>2008年成长最快的还是技术方面，PHP还算熟悉了，Javascript也凑合了，抽空做了做Python，已经离不开了，数据挖掘主要方向是推荐系统，也有些收获，过几天把<a href="http://www.netflixprize.com/" target="_blank">netflix prize</a>的分数晒晒。</p>
<p>与人交往方面还是没什么进步，基本上关系好的是我尊重的人和尊重我的人，所以你要成为我的好朋友，你需要有值得我尊重的东西，尤其是品质，或者尊重我，这样会友好很多。如果你不在二者之列，太对不起了，过去的一年我估计没少得罪你。</p>
<p>我出来之后有人问我出来感觉怎么样或者出来好不好，正好昨天看到一则小故事，人问佛：我不知道我现在的爱是真的还是假的，我该怎么办？佛说：有机会就去爱吧，不要管是劫还是缘，劫也是缘。所以有机会就试试吧，反正我的感觉很好。</p>
<p>2009年主要要做的事情：网络(socket)/桌面(gui)编程；一个精巧的PHP框架(雏形差不多了，和原生的PHP相比性能损耗大概在1.5ms左右)；数据挖掘(推荐系统差不多了，试试其他的领域，有可能在XX预测的方向)；多一些好朋友。</p>
<p>春天快要来了，因为人们都后知后觉，2009，大家都会是好样的。</p>
]]></content:encoded>
			<wfw:commentRss>http://chaoqun.17348.com/2008/12/my-2008/feed/</wfw:commentRss>
		</item>
		<item>
		<title>用Python创建线程池</title>
		<link>http://chaoqun.17348.com/2008/12/python-thread-pool/</link>
		<comments>http://chaoqun.17348.com/2008/12/python-thread-pool/#comments</comments>
		<pubDate>Tue, 23 Dec 2008 07:43:22 +0000</pubDate>
		<dc:creator>超群.com</dc:creator>
		
		<category><![CDATA[Python]]></category>

		<category><![CDATA[thread pool]]></category>

		<guid isPermaLink="false">http://chaoqun.17348.com/?p=115</guid>
		<description><![CDATA[本博客所有原创文章采用知识共享署名-非商业性使用-相同方式共享，转载请保留链接http://chaoqun.17348.com/2008/12/python-thread-pool/
最近在做一些数据挖掘方面的工作，需要对海量的数据进行处理，在目前的硬件环境下，多进程＋多线程的方式对运算时间的减少大有裨益，我用的是Python语言，开发效率高，运算效率也不低。
Python里面成型的线程池可以看以下http://www.chrisarndt.de/projects/threadpool/，看了一下API介绍，应该写的比较完备了，我这里想介绍的是Python线程池实现的原理以及一个简明的线程池代码实例。
我这里是用Queue这个包来实现的，Queue翻译成中文就是队列，全局的，我们把任务放进队列中去，然后开N个线程，每个线程都去队列中取一个任务，执行完了之后告诉系统说我执行完了，然后接着去队列中取下一个任务，直至队列中所有任务取空，退出线程。
这就是一般的线程池实现的原理，下面看一个实际的代码：

import time
import threading
import Queue
class Worker(threading.Thread):
    def __init__(self, name, queue):
        threading.Thread.__init__(self)
        self.queue = queue
        self.start()
    def run(self):
        # 著名的死循环，保证接着跑下一个任务
        while True:
            # 队列为空则退出线程
            if self.queue.empty():
                break
            # 获取一个项目
            foo = self.queue.get()
            # 延时1S模拟你要做的事情
            time.sleep(1)
            # 打印
            print self.getName(),':', foo
            # 告诉系统说任务完成
            self.queue.task_done()
# 队列
queue = Queue.Queue()
# 加入100个任务队列
for i in range(100):
    [...]]]></description>
			<content:encoded><![CDATA[<blockquote><p>本博客所有原创文章采用<a href="http://creativecommons.org/licenses/by-nc-sa/2.5/cn/" target="_blank"><span style="color: #356aa0;">知识共享署名-非商业性使用-相同方式共享</span></a>，转载请保留链接<a href="http://chaoqun.17348.com/2008/12/python-thread-pool/">http://chaoqun.17348.com/2008/12/python-thread-pool/</a></p></blockquote>
<p>最近在做一些数据挖掘方面的工作，需要对海量的数据进行处理，在目前的硬件环境下，多进程＋多线程的方式对运算时间的减少大有裨益，我用的是Python语言，开发效率高，运算效率也不低。</p>
<p>Python里面成型的线程池可以看以下<a href="http://www.chrisarndt.de/projects/threadpool/" target="_blank">http://www.chrisarndt.de/projects/threadpool/</a>，看了一下API介绍，应该写的比较完备了，我这里想介绍的是Python线程池实现的原理以及一个简明的线程池代码实例。</p>
<p>我这里是用Queue这个包来实现的，Queue翻译成中文就是队列，全局的，<strong>我们把任务放进队列中去，然后开N个线程，每个线程都去队列中取一个任务，执行完了之后告诉系统说我执行完了，然后接着去队列中取下一个任务，直至队列中所有任务取空，退出线程。</strong></p>
<p>这就是一般的线程池实现的原理，下面看一个实际的代码：</p>
<blockquote>
<pre>import time
import threading
import Queue</pre>
<pre>class Worker(threading.Thread):
    def __init__(self, name, queue):
        threading.Thread.__init__(self)
        self.queue = queue
        self.start()</pre>
<pre>    def run(self):
        # 著名的死循环，保证接着跑下一个任务
        while True:
            # 队列为空则退出线程
            if self.queue.empty():
                break</pre>
<pre>            # 获取一个项目
            foo = self.queue.get()</pre>
<pre>            # 延时1S模拟你要做的事情
            time.sleep(1)</pre>
<pre>            # 打印
            print self.getName(),':', foo</pre>
<pre>            # 告诉系统说任务完成
            self.queue.task_done()</pre>
<pre># 队列
queue = Queue.Queue()</pre>
<pre># 加入100个任务队列
for i in range(100):
    queue.put(i)</pre>
<pre># 开10个线程
for i in range(10):
    threadName = 'Thread' + str(i)
    Worker(threadName, queue)</pre>
<pre># 所有线程执行完毕后关闭
queue.join()</pre>
</blockquote>
<p>那些成型的代码都非常的复杂，想了解实现原理难度颇大，希望这篇短文能起到拨开云雾的功用。</p>
]]></content:encoded>
			<wfw:commentRss>http://chaoqun.17348.com/2008/12/python-thread-pool/feed/</wfw:commentRss>
		</item>
		<item>
		<title>MySQL统计函数GROUP_CONCAT陷阱</title>
		<link>http://chaoqun.17348.com/2008/12/mysql-trap-of-group-concat/</link>
		<comments>http://chaoqun.17348.com/2008/12/mysql-trap-of-group-concat/#comments</comments>
		<pubDate>Sat, 13 Dec 2008 06:42:20 +0000</pubDate>
		<dc:creator>超群.com</dc:creator>
		
		<category><![CDATA[默认分类]]></category>

		<guid isPermaLink="false">http://chaoqun.17348.com/?p=105</guid>
		<description><![CDATA[本博客所有原创文章采用知识共享署名-非商业性使用-相同方式共享，转载请保留链接http://chaoqun.17348.com/2008/12/mysql-trap-of-group-concat/
最近在用MySQL做一些数据的预处理，经常会用到group_concat函数，比如类似下面一条语句
mysql&#62;select aid,group_concat(bid) from tbl group by aid limit 1;
sql语句比较简单，按照aid分组，并且把相应的bid用逗号串起来。这样的句子大家可能都用过，也可能不会出问题，但是如果bid非常多的话，你就要小心了，比如下面的提示信息：
Query OK, XXX rows affected, 1 warning (3 min 45.12 sec)
怎么会有警告呢，打出来看看：

mysql&#62; show warnings;
+---------+------+-----------------------------------------+
&#124; Level   &#124; Code &#124; Message                                 &#124;
+---------+------+-----------------------------------------+
&#124; Warning &#124; 1260 &#124; 1 line(s) were cut by GROUP_CONCAT()    &#124;
+---------+------+-----------------------------------------+

 居然被GROUP_CONCAT截断了我的结果，查了一下手册，原来GROUP_CONCAT有个最大长度的限制，超过最大长度就会被截断掉，你可以通过下面的语句获得：

mysql&#62; SELECT @@global.group_concat_max_len;
+-------------------------------+
&#124; @@global.group_concat_max_len &#124;
+-------------------------------+
&#124;                      1024     &#124;
+-------------------------------+

1024这就是一般MySQL系统默认的最大长度，如果你的bid串起来大于这个就会出问题，好在有解决的办法：
1.在MySQL配置文件中加上
group_concat_max_len = 102400 #你要的最大长度
2.可以简单一点，执行语句：

mysql&#62; SET GLOBAL [...]]]></description>
			<content:encoded><![CDATA[<blockquote><p>本博客所有原创文章采用<a href="http://creativecommons.org/licenses/by-nc-sa/2.5/cn/" target="_blank"><span style="color: #356aa0;">知识共享署名-非商业性使用-相同方式共享</span></a>，转载请保留链接<a href="http://chaoqun.17348.com/2008/12/mysql-trap-of-group-concat/" target="_blank">http://chaoqun.17348.com/2008/12/mysql-trap-of-group-concat/</a></p></blockquote>
<p>最近在用MySQL做一些数据的预处理，经常会用到group_concat函数，比如类似下面一条语句</p>
<blockquote><p>mysql&gt;select aid,group_concat(bid) from tbl group by aid limit 1;</p></blockquote>
<p>sql语句比较简单，按照aid分组，并且把相应的bid用逗号串起来。这样的句子大家可能都用过，也可能不会出问题，但是如果bid非常多的话，你就要小心了，比如下面的提示信息：</p>
<blockquote><p>Query OK, XXX rows affected, 1 warning (3 min 45.12 sec)</p></blockquote>
<p>怎么会有警告呢，打出来看看：</p>
<blockquote>
<pre>mysql&gt; show warnings;
+---------+------+-----------------------------------------+
| Level   | Code | Message                                 |
+---------+------+-----------------------------------------+
| Warning | 1260 | 1 line(s) were cut by GROUP_CONCAT()    |
+---------+------+-----------------------------------------+</pre>
</blockquote>
<p> 居然被GROUP_CONCAT截断了我的结果，查了一下手册，原来GROUP_CONCAT有个最大长度的限制，超过最大长度就会被截断掉，你可以通过下面的语句获得：</p>
<blockquote>
<pre>mysql&gt; SELECT @@global.group_concat_max_len;
+-------------------------------+
| @@global.group_concat_max_len |
+-------------------------------+
|                      1024     |
+-------------------------------+</pre>
</blockquote>
<p>1024这就是一般MySQL系统默认的最大长度，如果你的bid串起来大于这个就会出问题，好在有解决的办法：</p>
<p>1.在MySQL配置文件中加上</p>
<blockquote><p>group_concat_max_len = 102400 #你要的最大长度</p></blockquote>
<p>2.可以简单一点，执行语句：</p>
<blockquote>
<pre>mysql&gt; SET GLOBAL group_concat_max_len=102400;
Query OK, 0 rows affected (0.01 sec)</pre>
</blockquote>
<p>再执行group_concat语句就不会出问题了，另外手册上还给出了group_concat的详细用法，给个示例你就明白了：</p>
<blockquote>
<pre>mysql&gt;select aid,group_concat(bid order by bid separator ',') as bid_str from tbl group by aid;</pre>
</blockquote>
<p>还可以排序和设置分隔符，功能强大。</p>
]]></content:encoded>
			<wfw:commentRss>http://chaoqun.17348.com/2008/12/mysql-trap-of-group-concat/feed/</wfw:commentRss>
		</item>
		<item>
		<title>Sphinx在windows下安装使用[支持中文全文检索]</title>
		<link>http://chaoqun.17348.com/2008/11/sphinx-on-windows-xp/</link>
		<comments>http://chaoqun.17348.com/2008/11/sphinx-on-windows-xp/#comments</comments>
		<pubDate>Wed, 26 Nov 2008 07:29:23 +0000</pubDate>
		<dc:creator>超群.com</dc:creator>
		
		<category><![CDATA[Full Text Sarch]]></category>

		<category><![CDATA[Open Source]]></category>

		<category><![CDATA[默认分类]]></category>

		<category><![CDATA[chinese]]></category>

		<category><![CDATA[sphinx]]></category>

		<guid isPermaLink="false">http://chaoqun.17348.com/?p=106</guid>
		<description><![CDATA[本博客所有原创文章采用知识共享署名-非商业性使用-相同方式共享，转载请保留链接http://chaoqun.17348.com/2008/11/sphinx-on-windows-xp/
前一阵子尝试使用了一下Sphinx，一个能够被各种语言(PHP/Python/Ruby/etc)方便调用的全文检索系统。网上的资料大多是在linux环境下的安装使用，当然，作为生产环境很有必要部署在*nix环境下，作为学习测试，还是windows环境比较方便些。
本文旨在提供一种便捷的方式让Sphinx在windows下安装配置以支持中文全文检索，配置部分在linux下通用。
一、关于Sphinx
Sphinx 是一个在GPLv2 下发布的一个全文检索引擎，商业授权（例如, 嵌入到其他程序中）需要联系作者（Sphinxsearch.com）以获得商业授权。
一般而言，Sphinx是一个独立的搜索引擎，意图为其他应用提供高速、低空间占用、高结果相关度的全文搜索功能。Sphinx可以非常容易的与SQL数据库和脚本语言集成。
当前系统内置MySQL和PostgreSQL 数据库数据源的支持，也支持从标准输入读取特定格式的XML数据。通过修改源代码，用户可以自行增加新的数据源（例如：其他类型的DBMS的原生支持）。
搜索API支持PHP、Python、Perl、Rudy和Java，并且也可以用作MySQL存储引擎。搜索API非常简单，可以在若干个小时之内移植到新的语言上。
Sphinx特性：

高速的建立索引(在当代CPU上，峰值性能可达到10MB/秒);
高性能的搜索(在2–4GB的文本数据上，平均每次检索响应时间小于0.1秒);
可处理海量数据(目前已知可以处理超过100GB的文本数据,在单一CPU的系统上可处理100M文档);
提供了优秀的相关度算法，基于短语相似度和统计（BM25）的复合Ranking方法;
支持分布式搜索;

提供文件的摘录生成;


可作为MySQL的存储引擎提供搜索服务;


支持布尔、短语、词语相似度等多种检索模式;


文档支持多个全文检索字段(最大不超过32个);


文档支持多个额外的属性信息(例如：分组信息，时间戳等);


停止词查询;


支持单一字节编码和UTF-8编码;


原生的MySQL支持(同时支持MyISAM和InnoDB);


原生的PostgreSQL支持.


中文手册可以在这里获得，感谢译者的辛勤工作。
二、Sphinx在windows上的安装
1.直接在http://www.sphinxsearch.com/downloads.html找到最新的windows版本，我这里下的是Win32 release binaries with MySQL support，下载后解压在D:\sphinx目录下；
2.在D:\sphinx\下新建一个data目录用来存放索引文件，一个log目录方日志文件，复制D:\sphinx\sphinx.conf.in到D:\sphinx\bin\sphinx.conf（注意修改文件名）；
3.修改D:\sphinx\bin\sphinx.conf，我这里列出需要修改的几个：

type        = mysql # 数据源，我这里是mysql
sql_host    = localhost # 数据库服务器
sql_user    = root # 数据库用户名
sql_pass    = '' # 数据库密码
sql_db      = test # 数据库
sql_port    = 3306 # 数据库端口
sql_query_pre   = SET NAMES utf8 # 去掉此行前面的注释，如果你的数据库是uft8编码的
index test1
{
# 放索引的目录
 path   = D:/sphinx/data/
# 编码
 charset_type  = utf-8
 #  指定utf-8的编码表
 charset_table  = 0..9, A..Z-&#62;a..z, _, a..z, U+410..U+42F-&#62;U+430..U+44F, U+430..U+44F
 # 简单分词，只支持0和1，如果要搜索中文，请指定为1
 ngram_len    = 1
# 需要分词的字符，如果要搜索中文，去掉前面的注释
 ngram_chars   = U+3000..U+2FA1F
}
# index test1stemmed : [...]]]></description>
			<content:encoded><![CDATA[<blockquote><p>本博客所有原创文章采用<a href="http://creativecommons.org/licenses/by-nc-sa/2.5/cn/" target="_blank"><span style="color: #356aa0;">知识共享署名-非商业性使用-相同方式共享</span></a>，转载请保留链接<a href="http://chaoqun.17348.com/2008/11/sphinx-on-windows-xp/" target="_blank">http://chaoqun.17348.com/2008/11/sphinx-on-windows-xp/</a></p></blockquote>
<p>前一阵子尝试使用了一下Sphinx，一个能够被各种语言(PHP/Python/Ruby/etc)方便调用的全文检索系统。网上的资料大多是在linux环境下的安装使用，当然，作为生产环境很有必要部署在*nix环境下，作为学习测试，还是windows环境比较方便些。</p>
<p>本文旨在提供一种便捷的方式让Sphinx在windows下安装配置以支持中文全文检索，配置部分在linux下通用。</p>
<p>一、关于Sphinx</p>
<p>Sphinx 是一个在GPLv2 下发布的一个全文检索引擎，商业授权（例如, 嵌入到其他程序中）需要联系作者（Sphinxsearch.com）以获得商业授权。</p>
<p>一般而言，Sphinx是一个独立的搜索引擎，意图为其他应用提供高速、低空间占用、高结果相关度的全文搜索功能。Sphinx可以非常容易的与SQL数据库和脚本语言集成。</p>
<p>当前系统内置MySQL和PostgreSQL 数据库数据源的支持，也支持从标准输入读取特定格式的XML数据。通过修改源代码，用户可以自行增加新的数据源（例如：其他类型的DBMS的原生支持）。</p>
<p>搜索API支持PHP、Python、Perl、Rudy和Java，并且也可以用作MySQL存储引擎。搜索API非常简单，可以在若干个小时之内移植到新的语言上。</p>
<p>Sphinx特性：</p>
<ul>
<li>高速的建立索引(在当代CPU上，峰值性能可达到10MB/秒);</li>
<li>高性能的搜索(在2–4GB的文本数据上，平均每次检索响应时间小于0.1秒);</li>
<li>可处理海量数据(目前已知可以处理超过100GB的文本数据,在单一CPU的系统上可处理100M文档);</li>
<li>提供了优秀的相关度算法，基于短语相似度和统计（BM25）的复合Ranking方法;</li>
<li>支持分布式搜索;</li>
<li>
<div id="result_box" style="TEXT-ALIGN: left" dir="ltr">提供文件的摘录生成;</div>
</li>
<li>
<div style="TEXT-ALIGN: left" dir="ltr">可作为MySQL的存储引擎提供搜索服务;</div>
</li>
<li>
<div style="TEXT-ALIGN: left" dir="ltr">支持布尔、短语、词语相似度等多种检索模式;</div>
</li>
<li>
<div style="TEXT-ALIGN: left" dir="ltr">文档支持多个全文检索字段(最大不超过32个);</div>
</li>
<li>
<div style="TEXT-ALIGN: left" dir="ltr">文档支持多个额外的属性信息(例如：分组信息，时间戳等);</div>
</li>
<li>
<div style="TEXT-ALIGN: left" dir="ltr">停止词查询;</div>
</li>
<li>
<div style="TEXT-ALIGN: left" dir="ltr">支持单一字节编码和UTF-8编码;</div>
</li>
<li>
<div style="TEXT-ALIGN: left" dir="ltr">原生的MySQL支持(同时支持MyISAM和InnoDB);</div>
</li>
<li>
<div style="TEXT-ALIGN: left" dir="ltr">原生的PostgreSQL支持.</div>
</li>
</ul>
<p style="TEXT-ALIGN: left" dir="ltr">中文手册可以在<a href="http://www.coreseek.com/uploads/pdf/sphinx_doc_zhcn_0.9.pdf" target="_blank">这里</a>获得，感谢<a href="http://www.coreseek.com" target="_blank">译者</a>的辛勤工作。</p>
<p style="TEXT-ALIGN: left" dir="ltr">二、Sphinx在windows上的安装</p>
<p style="TEXT-ALIGN: left" dir="ltr">1.直接在<a href="http://www.sphinxsearch.com/downloads.html" target="_blank">http://www.sphinxsearch.com/downloads.html</a>找到最新的windows版本，我这里下的是<a href="http://www.sphinxsearch.com/downloads/sphinx-0.9.8.1-win32.zip" target="_blank">Win32 release binaries with MySQL support</a>，下载后解压在D:\sphinx目录下；</p>
<p style="TEXT-ALIGN: left" dir="ltr">2.在D:\sphinx\下新建一个data目录用来存放索引文件，一个log目录方日志文件，复制D:\sphinx\sphinx.conf.in到D:\sphinx\bin\sphinx.conf（注意修改文件名）；</p>
<p style="TEXT-ALIGN: left" dir="ltr">3.修改D:\sphinx\bin\sphinx.conf，我这里列出需要修改的几个：</p>
<blockquote>
<pre style="TEXT-ALIGN: left" dir="ltr">type        = mysql # 数据源，我这里是mysql
sql_host    = localhost # 数据库服务器
sql_user    = root # 数据库用户名
sql_pass    = '' # 数据库密码
sql_db      = test # 数据库
sql_port    = 3306 # 数据库端口</pre>
<pre style="TEXT-ALIGN: left" dir="ltr">sql_query_pre   = SET NAMES utf8 # 去掉此行前面的注释，如果你的数据库是uft8编码的</pre>
<pre style="TEXT-ALIGN: left" dir="ltr">index test1
{
# 放索引的目录
 path   = D:/sphinx/data/
# 编码
 charset_type  = utf-8
 #  指定utf-8的编码表
 charset_table  = 0..9, A..Z-&gt;a..z, _, a..z, U+410..U+42F-&gt;U+430..U+44F, U+430..U+44F
 # 简单分词，只支持0和1，如果要搜索中文，请指定为1
 ngram_len    = 1
# 需要分词的字符，如果要搜索中文，去掉前面的注释
 ngram_chars   = U+3000..U+2FA1F
}</pre>
<pre style="TEXT-ALIGN: left" dir="ltr"># index test1stemmed : test1
# {
 # path   = @CONFDIR@/data/test1stemmed
 # morphology  = stem_en
# }

# 如果没有分布式索引，注释掉下面的内容

# index dist1
# {
 # 'distributed' index type MUST be specified
 # type    = distributed</pre>
<pre style="TEXT-ALIGN: left" dir="ltr"> # local index to be searched
 # there can be many local indexes configured
 # local    = test1
 # local    = test1stemmed</pre>
<pre style="TEXT-ALIGN: left" dir="ltr"> # remote agent
 # multiple remote agents may be specified
 # syntax is 'hostname:port:index1,[index2[,...]]
 # agent    = localhost:3313:remote1
 # agent    = localhost:3314:remote2,remote3</pre>
<pre style="TEXT-ALIGN: left" dir="ltr"> # remote agent connection timeout, milliseconds
 # optional, default is 1000 ms, ie. 1 sec
 # agent_connect_timeout = 1000</pre>
<pre style="TEXT-ALIGN: left" dir="ltr"> # remote agent query timeout, milliseconds
 # optional, default is 3000 ms, ie. 3 sec
 # agent_query_timeout  = 3000
# }</pre>
<pre style="TEXT-ALIGN: left" dir="ltr"># 搜索服务需要修改的部分
searchd
{
 # 日志
 log     = D:/sphinx/log/searchd.log</pre>
<pre style="TEXT-ALIGN: left" dir="ltr"> # PID file, searchd process ID file name
 pid_file   = D:/sphinx/log/searchd.pid</pre>
<pre style="TEXT-ALIGN: left" dir="ltr"> # windows下启动searchd服务一定要注释掉这个
 # seamless_rotate  = 1
}</pre>
</blockquote>
<p>4.导入测试数据</p>
<p>C:\Program Files\MySQL\MySQL Server 5.0\bin&gt;mysql -uroot test&lt;d:/sphinx/example.sql</p>
<p>5.建立索引</p>
<blockquote><p>D:\sphinx\bin&gt;indexer.exe &#8211;all<br />
Sphinx 0.9.8-release (r1533)<br />
Copyright (c) 2001-2008, Andrew Aksyonoff</p>
<p>using config file &#8216;./sphinx.conf&#8217;&#8230;<br />
indexing index &#8216;test1&#8242;&#8230;<br />
collected 4 docs, 0.0 MB<br />
sorted 0.0 Mhits, 100.0% done<br />
total 4 docs, 193 bytes<br />
total 0.101 sec, 1916.30 bytes/sec, 39.72 docs/sec</p>
<p>D:\sphinx\bin&gt;</p></blockquote>
<p>6.搜索&#8217;test&#8217;试试</p>
<blockquote><p>D:\sphinx\bin&gt;search.exe test<br />
Sphinx 0.9.8-release (r1533)<br />
Copyright (c) 2001-2008, Andrew Aksyonoff</p>
<p>using config file &#8216;./sphinx.conf&#8217;&#8230;<br />
index &#8216;test1&#8242;: query &#8216;test &#8216;: returned 3 matches of 3 total in 0.000 sec</p>
<p>displaying matches:<br />
1. document=1, weight=2, group_id=1, date_added=Wed Nov 26 14:58:59 2008<br />
        id=1<br />
        group_id=1<br />
        group_id2=5<br />
        date_added=2008-11-26 14:58:59<br />
        title=test one<br />
        content=this is my test document number one. also checking search within<br />
 phrases.<br />
2. document=2, weight=2, group_id=1, date_added=Wed Nov 26 14:58:59 2008<br />
        id=2<br />
        group_id=1<br />
        group_id2=6<br />
        date_added=2008-11-26 14:58:59<br />
        title=test two<br />
        content=this is my test document number two<br />
3. document=4, weight=1, group_id=2, date_added=Wed Nov 26 14:58:59 2008<br />
        id=4<br />
        group_id=2<br />
        group_id2=8<br />
        date_added=2008-11-26 14:58:59<br />
        title=doc number four<br />
        content=this is to test groups</p>
<p>words:<br />
1. &#8216;test&#8217;: 3 documents, 5 hits<br />
D:\sphinx\bin&gt;</p></blockquote>
<p>都所出来了吧。</p>
<p>6.测试中文搜索</p>
<p>修改test数据库中documents数据表，</p>
<blockquote><p>UPDATE `test`.`documents` SET `title` = &#8216;测试中文&#8217;, `content` = &#8216;this is my test document number two，应该搜的到吧&#8217; WHERE `documents`.`id` = 2;</p></blockquote>
<p>重建索引：</p>
<p>D:\sphinx\bin&gt;indexer.exe &#8211;all</p>
<p>搜索&#8217;中文&#8217;试试：</p>
<blockquote><p>D:\sphinx\bin&gt;search.exe 中文<br />
Sphinx 0.9.8-release (r1533)<br />
Copyright (c) 2001-2008, Andrew Aksyonoff</p>
<p>using config file &#8216;./sphinx.conf&#8217;&#8230;<br />
index &#8216;test1&#8242;: query &#8216;中文 &#8216;: returned 0 matches of 0 total in 0.000 sec</p>
<p>words:<br />
D:\sphinx\bin&gt;</p></blockquote>
<p>貌似没有搜到，这是因为windows命令行中的编码是gbk，当然搜不出来。我们可以用程序试试，在D:\sphinx\api下新建一个foo.php的文件，注意utf-8编码</p>
<blockquote><p>&lt;?php<br />
require &#8217;sphinxapi.php&#8217;;<br />
$s = new SphinxClient();<br />
$s-&gt;SetServer(&#8217;localhost&#8217;,3312);<br />
$result = $s-&gt;Query(&#8217;中文&#8217;);<br />
var_dump($result);<br />
?&gt;</p></blockquote>
<p>启动Sphinx searchd服务</p>
<blockquote><p>D:\sphinx\bin&gt;searchd.exe<br />
Sphinx 0.9.8-release (r1533)<br />
Copyright (c) 2001-2008, Andrew Aksyonoff</p>
<p>WARNING: forcing &#8211;console mode on Windows<br />
using config file &#8216;./sphinx.conf&#8217;&#8230;<br />
creating server socket on 0.0.0.0:3312<br />
accepting connections</p></blockquote>
<p>执行PHP查询：</p>
<blockquote><p>php d:/sphinx/api/foo.php</p></blockquote>
<p>结果是不是出来？剩下的工作就是去看手册，慢慢摸索高阶的配置。</p>
]]></content:encoded>
			<wfw:commentRss>http://chaoqun.17348.com/2008/11/sphinx-on-windows-xp/feed/</wfw:commentRss>
		</item>
		<item>
		<title>MySQL数据类型迷惑之整型xxxINT</title>
		<link>http://chaoqun.17348.com/2008/11/mysql-data-types-int/</link>
		<comments>http://chaoqun.17348.com/2008/11/mysql-data-types-int/#comments</comments>
		<pubDate>Fri, 07 Nov 2008 03:54:50 +0000</pubDate>
		<dc:creator>超群.com</dc:creator>
		
		<category><![CDATA[Mysql]]></category>

		<category><![CDATA[data_types]]></category>

		<guid isPermaLink="false">http://chaoqun.17348.com/?p=100</guid>
		<description><![CDATA[本博客所有原创文章采用知识共享署名-非商业性使用-相同方式共享，转载请保留链接http://chaoqun.17348.com/2008/11/mysql-data-types-int/
最近在做一些利用MySQL进行数据挖掘方面的尝试，处理的大多是海量的数据（一般是5000W条以上），由于数据量巨大，数据库表字段数据类型的选择就显示出重要性来了。
比如有下面的一个表：

mysql&#62; desc test;
+--------+---------+------+-----+---------+-------+
&#124; Field  &#124; Type    &#124; Null &#124; Key &#124; Default &#124; Extra &#124;
+--------+---------+------+-----+---------+-------+
&#124; uid    &#124; int(11) &#124; NO   &#124;     &#124; NULL    &#124;       &#124;
&#124; cid    &#124; int(11) &#124; NO   &#124;     &#124; NULL    &#124;       &#124;
&#124; rating &#124; int(11) &#124; NO   &#124;     &#124; NULL    &#124;       &#124;
&#124; day    &#124; date    &#124; NO   &#124;     &#124; NULL    &#124;       &#124;
+--------+---------+------+-----+---------+-------+
4 [...]]]></description>
			<content:encoded><![CDATA[<blockquote><p>本博客所有原创文章采用<a href="http://creativecommons.org/licenses/by-nc-sa/2.5/cn/" target="_blank"><span style="color: #356aa0;">知识共享署名-非商业性使用-相同方式共享</span></a>，转载请保留链接<a href="http://chaoqun.17348.com/2008/11/mysql-data-types-int/">http://chaoqun.17348.com/2008/11/mysql-data-types-int/</a></p></blockquote>
<p>最近在做一些利用MySQL进行数据挖掘方面的尝试，处理的大多是海量的数据（一般是5000W条以上），由于数据量巨大，数据库表字段数据类型的选择就显示出重要性来了。</p>
<p>比如有下面的一个表：</p>
<blockquote>
<pre>mysql&gt; desc test;
+--------+---------+------+-----+---------+-------+
| Field  | Type    | Null | Key | Default | Extra |
+--------+---------+------+-----+---------+-------+
| uid    | int(11) | NO   |     | NULL    |       |
| cid    | int(11) | NO   |     | NULL    |       |
| rating | int(11) | NO   |     | NULL    |       |
| day    | date    | NO   |     | NULL    |       |
+--------+---------+------+-----+---------+-------+
4 rows in set (0.00 sec)</pre>
</blockquote>
<p>其中uid是用户ID，cid是内容ID，rating是打分值，取值是1～5，day是打分日期，就是这样一个表，往里面导入数据，大概1亿条，然后执行：</p>
<blockquote><p>mysql&gt; show table status like &#8216;test&#8217;;</p></blockquote>
<p>数据库大概1.5G大小，注意里面的Avg_row_length字段，这个字段的意思平均每行占用的字节数，test表每行占用字节数（行大小）是16（3个int是12个字节，一个date是3个字节，然后再加1就是16个字节）。好大的数据，我们的优化开始了。</p>
<p>查看最大的一个uid是多少</p>
<blockquote><p>mysql&gt; select max(uid) from test;</p></blockquote>
<p>发现最大的uid是2 649 429，只有7位数</p>
<p>查看最大的一个aid是多少</p>
<p>mysql&gt; select max(cid) from test;</p>
<p>发现最大的cid是17 770，区区5位数</p>
<p>rating字段只有1～5这5个值，一位就搞定了</p>
<p>于是更改了test表设计</p>
<blockquote>
<pre>mysql&gt; desc test;
+--------+---------+------+-----+---------+-------+
| Field  | Type    | Null | Key | Default | Extra |
+--------+---------+------+-----+---------+-------+
| uid    | int(7)  | NO   |     | NULL    |       |
| cid    | int(5)  | NO   |     | NULL    |       |
| rating | int(1)  | NO   |     | NULL    |       |
| day    | date    | NO   |     | NULL    |       |
+--------+---------+------+-----+---------+-------+
4 rows in set (0.00 sec)</pre>
</blockquote>
<p>重新测试一下，大状况了，还是1.5G，每行还是16个字节，怎么会这样？查了一下手册得知：<strong>int(m) m表示最大显示宽度，注意是显示宽度，不会影响它的取值范围</strong>，你大可以在int(1)的字段中插入9999的数字，m不会影响此列的取值范围，也就是说int(1)和int(11)占用的字节数是一样多的，你是不是和我一样想当然了？下面是手册上关于xxxINT类型的详细说明：</p>
<table class=" FCK__ShowTableBorders" border="0" cellspacing="0" cellpadding="0" width="600">
<tbody>
<tr>
<td style="width: 160px;">
<pre> MySQL数据类型</pre>
</td>
<td>
<pre> 含义</pre>
</td>
</tr>
<tr>
<td>
<pre> TINYINT(m)</pre>
</td>
<td>
<pre> 8位整数（1字节，取值范围-128～+127）；可选参数m表示最大显示宽度，对取值范围无影响，如果使用了UNSIGNED，则取值范围为0~255</pre>
</td>
</tr>
<tr>
<td>
<pre> SMALLINT(m)</pre>
</td>
<td>
<pre> 16位整数（2字节，取值范围-32 768～+32 767）</pre>
</td>
</tr>
<tr>
<td>
<pre> MEDIUMINT(m)</pre>
</td>
<td>
<pre> 24位整数（3字节，取值范围-8 388 608～+8 388 607）</pre>
</td>
</tr>
<tr>
<td>
<pre> INT(m)、INTERGER(m)</pre>
</td>
<td>
<pre> 32位整数（4字节，取值范围-2 147 483 648～+2 147 483 647）</pre>
</td>
</tr>
<tr>
<td>
<pre> BIGINT(m)</pre>
</td>
<td>
<pre> 64位整数（8字节，取值范围±9.22*1018</pre>
</td>
</tr>
<tr>
<td>
<pre> SERIAL</pre>
</td>
<td>
<pre> BIGINT AUTO_INCREMENT NOT NULL PRIMARY KEY的缩写</pre>
</td>
</tr>
</tbody>
</table>
<p>于是再次更改表的设计：</p>
<blockquote>
<pre>mysql&gt; desc test;
+--------+-----------------------+------+-----+---------+-------+
| Field  | Type                  | Null | Key | Default | Extra |
+--------+-----------------------+------+-----+---------+-------+
| uid    | mediumint(8) unsigned | NO   |     | NULL    |       |
| cid    | smallint(5) unsigned  | NO   |     | NULL    |       |
| rating | tinyint(1)            | NO   |     | NULL    |       |
| day    | date                  | NO   |     | NULL    |       |
+--------+-----------------------+------+-----+---------+-------+</pre>
</blockquote>
<p>再执行</p>
<p>mysql&gt; show table status like &#8216;test&#8217;;</p>
<p>test表大小变成不到960MB了，行大小变成10字节（3＋2＋1＋3＋1），苗条了许多。</p>
<p>另外，手册上说<strong>两个UNSIGNED的字段相减，值还是UNSIGNED</strong>，这就意味着如果用3-5的话，最后得到的结果是2而不是-2，注意安全。</p>
]]></content:encoded>
			<wfw:commentRss>http://chaoqun.17348.com/2008/11/mysql-data-types-int/feed/</wfw:commentRss>
		</item>
		<item>
		<title>周日混QClub手记</title>
		<link>http://chaoqun.17348.com/2008/10/qclub-ruby-on-rails/</link>
		<comments>http://chaoqun.17348.com/2008/10/qclub-ruby-on-rails/#comments</comments>
		<pubDate>Mon, 20 Oct 2008 03:14:07 +0000</pubDate>
		<dc:creator>超群.com</dc:creator>
		
		<category><![CDATA[Arch]]></category>

		<category><![CDATA[caibanzi]]></category>

		<category><![CDATA[rails]]></category>

		<category><![CDATA[ruby]]></category>

		<guid isPermaLink="false">http://chaoqun.17348.com/?p=97</guid>
		<description><![CDATA[本博客所有原创文章采用知识共享署名-非商业性使用-相同方式共享，转载请保留链接http://chaoqun.17348.com/2008/10/qclub-ruby-on-rails/
周日(2008-10-19)下午，去听了听QClub组织的Ruby网站架构案例分享──财帮子&#38;FreeWheel，本意是冲着架构案例分享去的，对Ruby On Rails知之甚少，很久以前和BLUG的一位外国朋友聊天，他说是做Ruby的，我说Ruby是日本人开发的语言，还没等我说完，这位外国的朋友说：It&#8217;s just a language,nothin to do with Japanese，其实我的本意是这是日本人开发的语言，国际化方面可能会差一些，比如最原始的文档应该是日文的吧。看来我们总是被人误会啊。
首先是财帮子的Robin Lu(陆亦斌)给我们分享了一下他们的建站经历，先介绍一下财帮子的基本情况：

财帮子目前大概是100万PV/天，注册用户大概十几万
人均访问PV大概在15～20个左右
网站访问的峰值大约在基金净值出来之后，最高大概是430 Rec/Sec
最开始只有一台1U的服务器，应用和数据库都在上面，现在两台1U的服务器，应用服务一台，数据库一台
系统构建在Ruby On Rails上
使用lighttpd做负载均衡，30个左右mongrel实例
使用Munin和monit做服务器监控，跑了一些的cron任务(如数据备份)

他们遇到的第一个问题是前端负载均衡效率不高，最开始用apache的mod_proxy_balancer，发现有阻塞的情况存在，现在用的是lighttpd做负载均衡，他们觉得效果不错，据说还毙掉了nginx的方案，由于久做PHP，印象中前端负载均衡应该是把请求均衡到多台服务器上，初一听他们的负载均衡是把请求均衡到多个实例上，觉得甚是费解，一打听才知道mongrel一次大概只能处理一个请求，不太了解，不好评论。
他们的第二个问题是发现Ruby On Rails里面的数据库操作有大的瓶颈，大概是对数据库的操作始终在一个事务中，不到最后不commit，所以导致死锁的情况屡有发生。这让我想起Rasmus Lerdorf老大对PHP Framework的批判Simple is Hard(用IE会被鄙视的)，Framework大大提高了开发的效率，却捆住了我们的双手。如果想要高的效率，那么就让ORM见鬼去吧。
他们还有一个好的建议是把动态内容和静态文件分开处理（当然这个建议已经被无数人说过了），这个确实非常有必要。
听完报告，问了下财帮子目前的瓶颈在什么地方，答曰：内存缺乏，跑了30个mongrel直接就吃掉了2G多的内存，确实够凶猛。这方面PHP算是不错了。
另外报告中还提到他们每天需要2～3小时去处理用户的账单，比如基金净值出来了，然后他们会计算每个用户的账单情况，这种做法不太看好，想想现在十几万用户可以算出来，将来多了呢？多台服务器并行计算？可计算成本呢？提前计算网站的活跃用户足够了，那些几百年才来一次的用户或者注册了再也不上的用户大可不必，等他们访问的时候再实时计算吧。
后面分享的FreeWheel讲的大多是项目开发方面的经验，我倒是觉得FreeWheel的做法不错，各种Framewok适合快速开发网站，不见得适合开发快速的网站。
感谢财帮子和FreeWheel的分享，技术其实就那么点事，还是开放一点好。
对Ruby On Rails实在了解甚少，上述内容如有不到之处，请不吝赐教。
]]></description>
			<content:encoded><![CDATA[<blockquote><p>本博客所有原创文章采用<a href="http://creativecommons.org/licenses/by-nc-sa/2.5/cn/" target="_blank"><span style="color: #356aa0;">知识共享署名-非商业性使用-相同方式共享</span></a>，转载请保留链接<a href="http://chaoqun.17348.com/2008/10/qclub-ruby-on-rails/">http://chaoqun.17348.com/2008/10/qclub-ruby-on-rails/</a></p></blockquote>
<p>周日(2008-10-19)下午，去听了听QClub组织的<a href="http://www.infoq.com/cn/news/2008/10/qclub-ruby-case-studies" target="_blank">Ruby网站架构案例分享──财帮子&amp;FreeWheel</a>，本意是冲着架构案例分享去的，对Ruby On Rails知之甚少，很久以前和<a href="http://www.beijinglug.org/" target="_blank">BLUG</a>的一位外国朋友聊天，他说是做Ruby的，我说Ruby是日本人开发的语言，还没等我说完，这位外国的朋友说：It&#8217;s just a language,nothin to do with Japanese，其实我的本意是这是日本人开发的语言，国际化方面可能会差一些，比如最原始的文档应该是日文的吧。看来我们总是被人误会啊。</p>
<p>首先是<a href="http://www.caibangzi.com/" target="_blank">财帮子</a>的<a href="http://www.robinlu.com/" target="_blank">Robin Lu</a>(陆亦斌)给我们分享了一下他们的建站经历，先介绍一下财帮子的基本情况：</p>
<ul>
<li>财帮子目前大概是100万PV/天，注册用户大概十几万</li>
<li>人均访问PV大概在15～20个左右</li>
<li>网站访问的峰值大约在基金净值出来之后，最高大概是430 Rec/Sec</li>
<li>最开始只有一台1U的服务器，应用和数据库都在上面，现在两台1U的服务器，应用服务一台，数据库一台</li>
<li>系统构建在Ruby On Rails上</li>
<li>使用<a href="http://www.lighttpd.net/" target="_blank">lighttpd</a>做负载均衡，30个左右<a href="http://mongrel.rubyforge.org/" target="_blank">mongrel</a>实例</li>
<li>使用<a href="http://munin.projects.linpro.no/" target="_blank">Munin</a>和monit做服务器监控，跑了一些的cron任务(如数据备份)</li>
</ul>
<p>他们遇到的第一个问题是前端负载均衡效率不高，最开始用apache的mod_proxy_balancer，发现有阻塞的情况存在，现在用的是lighttpd做负载均衡，他们觉得效果不错，据说还毙掉了<a href="http://nginx.net/" target="_blank">nginx</a>的方案，由于久做PHP，印象中前端负载均衡应该是把请求均衡到多台服务器上，初一听他们的负载均衡是把请求均衡到多个实例上，觉得甚是费解，一打听才知道mongrel一次大概只能处理一个请求，不太了解，不好评论。</p>
<p>他们的第二个问题是发现Ruby On Rails里面的数据库操作有大的瓶颈，大概是对数据库的操作始终在一个事务中，不到最后不commit，所以导致死锁的情况屡有发生。这让我想起Rasmus Lerdorf老大对PHP Framework的批判<a href="http://talks.php.net/show/cmu08" target="_blank">Simple is Hard</a>(用IE会被鄙视的)，Framework大大提高了开发的效率，却捆住了我们的双手。如果想要高的效率，那么就让ORM见鬼去吧。</p>
<p>他们还有一个好的建议是把动态内容和静态文件分开处理（当然这个建议已经被无数人说过了），这个确实非常有必要。</p>
<p>听完报告，问了下财帮子目前的瓶颈在什么地方，答曰：内存缺乏，跑了30个mongrel直接就吃掉了2G多的内存，确实够凶猛。这方面PHP算是不错了。</p>
<p>另外报告中还提到他们每天需要2～3小时去处理用户的账单，比如基金净值出来了，然后他们会计算每个用户的账单情况，这种做法不太看好，想想现在十几万用户可以算出来，将来多了呢？多台服务器并行计算？可计算成本呢？提前计算网站的活跃用户足够了，那些几百年才来一次的用户或者注册了再也不上的用户大可不必，等他们访问的时候再实时计算吧。</p>
<p>后面分享的<a href="http://www.freewheel.tv/" target="_blank">FreeWheel</a>讲的大多是项目开发方面的经验，我倒是觉得FreeWheel的做法不错，各种Framewok适合快速开发网站，不见得适合开发快速的网站。</p>
<p>感谢财帮子和FreeWheel的分享，技术其实就那么点事，还是开放一点好。</p>
<p>对Ruby On Rails实在了解甚少，上述内容如有不到之处，请不吝赐教。</p>
]]></content:encoded>
			<wfw:commentRss>http://chaoqun.17348.com/2008/10/qclub-ruby-on-rails/feed/</wfw:commentRss>
		</item>
		<item>
		<title>另一种分布式数据中心数据同步方案</title>
		<link>http://chaoqun.17348.com/2008/10/another-way-of-sync-distributed-data-center/</link>
		<comments>http://chaoqun.17348.com/2008/10/another-way-of-sync-distributed-data-center/#comments</comments>
		<pubDate>Wed, 15 Oct 2008 04:04:33 +0000</pubDate>
		<dc:creator>超群.com</dc:creator>
		
		<category><![CDATA[Arch]]></category>

		<category><![CDATA[默认分类]]></category>

		<category><![CDATA[memcached]]></category>

		<category><![CDATA[Mysql]]></category>

		<guid isPermaLink="false">http://chaoqun.17348.com/?p=90</guid>
		<description><![CDATA[本博客所有原创文章采用知识共享署名-非商业性使用-相同方式共享，转载请保留链接http://chaoqun.17348.com/2008/10/another-way-of-sync-distributed-data-center/
通过Google Reader好友共享看到这篇文章：横向扩展（Facebook），之前看过英文原文，不过仅粗读了一下未求甚解，感谢译者的辛勤劳动。
看完全文，大意是Facebook为了解决跨地域的网络延迟问题，在东海岸新建了一个数据中心，可新的问题随之而来，MySQL数据库复制延迟、memcache缓存不同步，Facebook的工程师通过改进MySQL复制机制，在传递binlog的同时传递memcache更新指令。这样数据库同步了，memcache也更新了。

我将我的名字从&#8221;Jason&#8221;改为&#8221;Monkey&#8221;。
我将&#8221;Monkey&#8221;写入加利福尼亚的主数据库并从加利福尼亚的memcache中删除我的名字，但不包括弗吉尼亚的memcache。
某个人在弗吉尼亚访问了我信息。在memcache中找到了我的名字，并返回&#8221;Jason&#8221;。（注意这里，其他用户看到了过时的信息）
同步复制到了之后，将从数据库中我的名字更新为&#8221;Monkey&#8221;。还需要从弗吉尼亚的memcache中删除我的名字因为缓存对象出现在同步复制流中了。
另一个人在弗吉尼亚访问了我的信息，没有在memcache中找到我的名字，所以从从数据库读出名字，得到了&#8221;Monkey&#8221;。

关键的地方就在这里，Facebook有能力对MySQL进行改造，相信一般的公司是不敢的。其实也还有另外一种方式来实现，MySQL引入了一种叫做MySQL UDF(user defined function)的机制，有人也写了个Memcached Functions for MySQL的东东（基于libmemcached，我想应该是稳定高效吧），我们大可以在从服务器上创建触发器，这样一旦有数据更新，调用Memcached Functions for MySQL更新memcached缓存(如果写操作频繁的话，估计触发器也是瓶颈)。
文章的另一个核心是“页面路径选择”，Facebook约定了更新只在主服务器上（西海岸数据中心），东海岸的数据中心数据库服务器全部以slave形式工作，Facebook在最前端有一组负载均衡服务器，更加用户访问的URL来判断是否有写操作，如果有的话直接连接到主服务器上，如果只是页面显示则导向到从服务器上（也许是西海岸服务器），为了解决数据延迟而造成混乱的问题，Facebook在有更新的用户cookie里面加入一个标记，该标记创建后20秒内，都连主服务器（20秒足够对付延迟了吧）。
这是很有意思的一个设计，利用cookie标记用户是否更新过。但问题就是只是自己看到自己最新的数据，其他人有个20秒看到的是过时的数据。
其实是不是可以试试把标记信息放服务器端（如DNS解析部分），比如A用户更新了某些数据，在服务器端做下标记，20秒内请求到这些资源的时候走主服务器。标记大可以用bdb存储，效率就不是问题了。下面是流程：

我将我的名字从&#8221;Jason&#8221;改为&#8221;Monkey&#8221;。
我将&#8221;Monkey&#8221;写入加利福尼亚的主数据库并从加利福尼亚的memcache中删除我的名字，但不包括弗吉尼亚的memcache。
在服务器端标记我的名字改变了。
某个人访问了我信息，查出我的信息更改过，走主服务器。
同步复制到了之后，将从数据库中我的名字更新为&#8221;Monkey&#8221;，触发器工作，删除相应memcache缓存。
20秒后，负载均衡端标记失效，另一个人在弗吉尼亚访问了我的信息，没有在memcache中找到我的名字，所以从从数据库读出名字，得到了&#8221;Monkey&#8221;。

也是很折腾的一种方案，我这里没有那样的环境，所以也只是理论上的思考，有条件的可以试试看效果如何。
]]></description>
			<content:encoded><![CDATA[<blockquote><p>本博客所有原创文章采用<a href="http://creativecommons.org/licenses/by-nc-sa/2.5/cn/" target="_blank"><span style="color: #356aa0;">知识共享署名-非商业性使用-相同方式共享</span></a>，转载请保留链接<a href="http://chaoqun.17348.com/2008/10/another-way-of-sync-distributed-data-center/">http://chaoqun.17348.com/2008/10/another-way-of-sync-distributed-data-center/</a></p></blockquote>
<p>通过Google Reader好友共享看到这篇文章：<a href="http://shiningray.cn/facebook-scaling-out.html" target="_blank">横向扩展（Facebook）</a>，之前看过英文原文，不过仅粗读了一下未求甚解，感谢<a href="http://shiningray.cn/" target="_blank">译者</a>的辛勤劳动。</p>
<p>看完全文，大意是Facebook为了解决跨地域的网络延迟问题，在东海岸新建了一个数据中心，可新的问题随之而来，MySQL数据库复制延迟、memcache缓存不同步，Facebook的工程师通过改进MySQL复制机制，在传递binlog的同时传递memcache更新指令。这样数据库同步了，memcache也更新了。</p>
<ul>
<li>我将我的名字从&#8221;Jason&#8221;改为&#8221;Monkey&#8221;。</li>
<li>我将&#8221;Monkey&#8221;写入加利福尼亚的主数据库并从加利福尼亚的memcache中删除我的名字，但不包括弗吉尼亚的memcache。</li>
<li>某个人在弗吉尼亚访问了我信息。在memcache中找到了我的名字，并返回&#8221;Jason&#8221;。（<strong>注意这里，其他用户看到了过时的信息</strong>）</li>
<li>同步复制到了之后，将从数据库中我的名字更新为&#8221;Monkey&#8221;。还需要从弗吉尼亚的memcache中删除我的名字因为缓存对象出现在同步复制流中了。</li>
<li>另一个人在弗吉尼亚访问了我的信息，没有在memcache中找到我的名字，所以从从数据库读出名字，得到了&#8221;Monkey&#8221;。</li>
</ul>
<p>关键的地方就在这里，Facebook有能力对MySQL进行改造，相信一般的公司是不敢的。其实也还有另外一种方式来实现，MySQL引入了一种叫做<a href="http://dev.mysql.com/doc/refman/5.0/en/adding-udf.html" target="_blank">MySQL UDF</a>(user defined function)的机制，有人也写了个<a href="http://tangent.org/586/Memcached_Functions_for_MySQL.html" target="_blank">Memcached Functions for MySQL</a>的东东（基于<a href="http://tangent.org/552/libmemcached.html" target="_blank">libmemcached</a>，我想应该是稳定高效吧），我们大可以在从服务器上创建触发器，这样一旦有数据更新，调用<a href="http://tangent.org/586/Memcached_Functions_for_MySQL.html" target="_blank">Memcached Functions for MySQL</a>更新memcached缓存(如果写操作频繁的话，估计触发器也是瓶颈)。</p>
<p>文章的另一个核心是“页面路径选择”，Facebook约定了更新只在主服务器上（西海岸数据中心），东海岸的数据中心数据库服务器全部以slave形式工作，Facebook在最前端有一组负载均衡服务器，更加用户访问的URL来判断是否有写操作，如果有的话直接连接到主服务器上，如果只是页面显示则导向到从服务器上（也许是西海岸服务器），为了解决数据延迟而造成混乱的问题，<strong>Facebook在有更新的用户cookie里面加入一个标记，该标记创建后20秒内，都连主服务器</strong>（20秒足够对付延迟了吧）。</p>
<p>这是很有意思的一个设计，利用cookie标记用户是否更新过。但问题就是只是自己看到自己最新的数据，其他人有个20秒看到的是过时的数据。</p>
<p>其实是不是可以试试把标记信息放服务器端（如DNS解析部分），比如A用户更新了某些数据，在服务器端做下标记，20秒内请求到这些资源的时候走主服务器。标记大可以用bdb存储，效率就不是问题了。下面是流程：</p>
<ul>
<li>我将我的名字从&#8221;Jason&#8221;改为&#8221;Monkey&#8221;。</li>
<li>我将&#8221;Monkey&#8221;写入加利福尼亚的主数据库并从加利福尼亚的memcache中删除我的名字，但不包括弗吉尼亚的memcache。</li>
<li>在<strong>服务器端标记</strong>我的名字改变了。</li>
<li>某个人访问了我信息，查出我的信息更改过，走主服务器。</li>
<li>同步复制到了之后，将从数据库中我的名字更新为&#8221;Monkey&#8221;，<strong>触发器工作</strong>，删除相应memcache缓存。</li>
<li>20秒后，负载均衡端标记失效，另一个人在弗吉尼亚访问了我的信息，没有在memcache中找到我的名字，所以从从数据库读出名字，得到了&#8221;Monkey&#8221;。</li>
</ul>
<p>也是很折腾的一种方案，我这里没有那样的环境，所以也只是理论上的思考，有条件的可以试试看效果如何。</p>
]]></content:encoded>
			<wfw:commentRss>http://chaoqun.17348.com/2008/10/another-way-of-sync-distributed-data-center/feed/</wfw:commentRss>
		</item>
		<item>
		<title>构建可扩展网络应用的七个步骤</title>
		<link>http://chaoqun.17348.com/2008/09/7-stages-scaling-of-web-apps/</link>
		<comments>http://chaoqun.17348.com/2008/09/7-stages-scaling-of-web-apps/#comments</comments>
		<pubDate>Sat, 27 Sep 2008 13:20:04 +0000</pubDate>
		<dc:creator>超群.com</dc:creator>
		
		<category><![CDATA[Arch]]></category>

		<category><![CDATA[默认分类]]></category>

		<category><![CDATA[scaling]]></category>

		<category><![CDATA[web]]></category>

		<guid isPermaLink="false">http://chaoqun.17348.com/?p=73</guid>
		<description><![CDATA[本博客所有原创文章采用知识共享署名-非商业性使用-相同方式共享，转载请保留链接http://chaoqun.17348.com/2008/09/7-stages-scaling-of-web-apps/
几天前看到The 7 Stages of Scaling Web Apps，觉得很是不错，想写点什么，奈何一直很忙，整天忙着那些quick&#38;dirty的项目，让人也堕落了许多。
今天找到报告的原文看了一下，内容丰满了许多。
要构建一个可扩展的网络应用,大多是数据库分表分库、分布式缓存、负载均衡、统一存储，诸如此类，前面我也翻译了一些国外网站架构文章，John Engales的文章高明之处是分7个步骤阐述了一个网络应用从简单到高度可扩展的构建过程。对比这七个步骤，想想我们的应用处在第几步呢？
报告的一开始解释了Performance（性能）和Scalability（扩展性）的区别，里面有两幅图片很好的诠释了，高性能就是买个好车（高配服务器）在高速公路（充足带宽）跑，高扩展性是一堆车跑在立交桥N通道上，不一定跑的很快，至少不会堵车。
第一步：开始阶段
简单的结构：防火墙＋负载均衡、一对服务器、数据库服务器、内部存储，结构简单，没有冗余，维护成本低，很是适合开始阶段。
这样的应用结构应该撑个50W～100W的访问没问题吧，一般的应用话。
很多的应用可能还不到这个水平，一台服务器扔在托管机房里，应用和数据库跑在同一台服务器上，这样的应该是“第零步”吧，如果要扩展这样的应用，升级到第一步吧，应该对程序和数据库不需要怎么修改，效果可能还会不错（如果你的应用不是特别大的话）。
第二步：和第一步一样，只是规模大了些
应用很成功，加更多防火墙、负载均衡，增加更多的应用服务器，找个DBA来优化一下数据库，增加数据库存储备份，把数据库存到SAN或者DAS（这个不太赞同，有几个有这个钱的啊），仍然是个简单的应用。
第二步也很有用，估计能撑到300W左右。如果钱能解决的问题，都不是问题，访问量大了，有钱了，多买点服务器能解决问题何乐而不为？
第三步：痛苦的开始
访问量大了，加个Squid或者Varnish做反向代理吧，或者更高级的负载均衡，以缓存静态内容。加更多的应用服务器，数据库做个Master-Slave，一台Master负责写操作，N台Slave负责读操作，可能会需要做一些代码修改。
到这一步就有点像模像样了，这应该是硬件方面能做到的极限吧，估计撑了500W左右问题不大。
第四步：更加痛苦了
访问量更大了，做个memcache分布式缓存吧，数据库主从复制出现问题了，大量的写堆积到一台服务器上，复制时间过长（数据库从服务器不能即时同步），然后可以做做数据库分区（或者分表），内容统一存储，需要大量重构应用和数据库结构。
终于引入memcache了，可以大大减轻服务器硬盘读取的负载，当数据库（数据库表）变的过大的时候，再多服务器都不管用，想想你的一个数据表有100G，索引有10个G，服务器内存才4个G，光放索引都放不下。这个时候分表会是一个不错的选择，简单的分表可以自定义一个hash函数或者按照ID主键分表，只是这种固定的分表方法将来数据迁移会费事很多，还是推荐Flickr的中心数据库做法，后面会讲到。
到第四步的规模，估计1000W问题不大了
第五步：真正的痛苦开始了
开始恐慌了，开始考虑整个应用模式了，仅仅是通过业务分区还不够，可以通过地理位置啊（比如北方的用户都用北京的服务器），用户ID啊什么的分服务器分区，建立服务器集群，对于每个集群，所有功能都是可用的（这样可以很好的做数据迁移以及冗余），使用一个hash结构或者主服务器来判断用户属于哪个服务器集群（^_^中心数据库）
第五步？估计很多应用都到不了这一步，已经分完善了，估计5000W左右都没问题。
第六步：一点点痛苦
扩展应用和数据库架构，提高性能，增加新功能，优化代码，访问还在增长，但是可控。
比较惬意的阶段了，1个亿的访问量轻松应对吧。
第七步：进入未知的领域
开始找系统的瓶颈了，电源、存储、CDN什么的，更多的数据中心，可能难题是夸地区的数据复制。
呵呵，这个应该是Google考虑的问题了.
想想我们的应用瓶颈在那里，不要一下子什么都想想全了，一步到位的做法是不存在的，知道那里不足，解决它，发现其他的不足，再解决，无休止的循环，直到你失业（什么问题都没有，还要你做什么啊）或者退休（该歇歇了）。
]]></description>
			<content:encoded><![CDATA[<blockquote><p>本博客所有原创文章采用<a href="http://creativecommons.org/licenses/by-nc-sa/2.5/cn/" target="_blank">知识共享署名-非商业性使用-相同方式共享</a>，转载请保留链接<a href="http://chaoqun.17348.com/2008/09/7-stages-scaling-of-web-apps/" target="_blank">http://chaoqun.17348.com/2008/09/7-stages-scaling-of-web-apps/</a></p></blockquote>
<p>几天前看到<a href="http://highscalability.com/7-stages-scaling-web-apps" target="_blank">The 7 Stages of Scaling Web Apps</a>，觉得很是不错，想写点什么，奈何一直很忙，整天忙着那些quick&amp;dirty的项目，让人也堕落了许多。</p>
<p>今天找到报告的<a href="http://www.slideshare.net/davemitz/7-stages-of-scaling-web-applications" target="_blank">原文</a>看了一下，内容丰满了许多。</p>
<p>要构建一个可扩展的网络应用,大多是数据库分表分库、分布式缓存、负载均衡、统一存储，诸如此类，前面我也翻译了一些国外网站架构文章，John Engales的文章高明之处是分7个步骤阐述了一个网络应用从简单到高度可扩展的构建过程。对比这七个步骤，想想我们的应用处在第几步呢？</p>
<p>报告的一开始解释了Performance（性能）和Scalability（扩展性）的区别，里面有两幅图片很好的诠释了，高性能就是买个好车（高配服务器）在高速公路（充足带宽）跑，高扩展性是一堆车跑在立交桥N通道上，不一定跑的很快，至少不会堵车。</p>
<p><strong>第一步：开始阶段</strong></p>
<p>简单的结构：防火墙＋负载均衡、一对服务器、数据库服务器、内部存储，结构简单，没有冗余，维护成本低，很是适合开始阶段。</p>
<p>这样的应用结构应该撑个50W～100W的访问没问题吧，一般的应用话。</p>
<p>很多的应用可能还不到这个水平，一台服务器扔在托管机房里，应用和数据库跑在同一台服务器上，这样的应该是“第零步”吧，如果要扩展这样的应用，升级到第一步吧，应该对程序和数据库不需要怎么修改，效果可能还会不错（如果你的应用不是特别大的话）。</p>
<p><strong>第二步：和第一步一样，只是规模大了些</strong></p>
<p>应用很成功，加更多防火墙、负载均衡，增加更多的应用服务器，找个DBA来优化一下数据库，增加数据库存储备份，把数据库存到SAN或者DAS（这个不太赞同，有几个有这个钱的啊），仍然是个简单的应用。</p>
<p>第二步也很有用，估计能撑到300W左右。如果钱能解决的问题，都不是问题，访问量大了，有钱了，多买点服务器能解决问题何乐而不为？</p>
<p><strong>第三步：痛苦的开始</strong></p>
<p>访问量大了，加个Squid或者Varnish做反向代理吧，或者更高级的负载均衡，以缓存静态内容。加更多的应用服务器，数据库做个Master-Slave，一台Master负责写操作，N台Slave负责读操作，可能会需要做一些代码修改。</p>
<p>到这一步就有点像模像样了，这应该是硬件方面能做到的极限吧，估计撑了500W左右问题不大。</p>
<p><strong>第四步：更加痛苦了</strong></p>
<p>访问量更大了，做个memcache分布式缓存吧，数据库主从复制出现问题了，大量的写堆积到一台服务器上，复制时间过长（数据库从服务器不能即时同步），然后可以做做数据库分区（或者分表），内容统一存储，需要大量重构应用和数据库结构。</p>
<p>终于引入memcache了，可以大大减轻服务器硬盘读取的负载，当数据库（数据库表）变的过大的时候，再多服务器都不管用，想想你的一个数据表有100G，索引有10个G，服务器内存才4个G，光放索引都放不下。这个时候分表会是一个不错的选择，简单的分表可以自定义一个hash函数或者按照ID主键分表，只是这种固定的分表方法将来数据迁移会费事很多，还是推荐Flickr的中心数据库做法，后面会讲到。</p>
<p>到第四步的规模，估计1000W问题不大了</p>
<p><strong>第五步：真正的痛苦开始了</strong></p>
<p>开始恐慌了，开始考虑整个应用模式了，仅仅是通过业务分区还不够，可以通过地理位置啊（比如北方的用户都用北京的服务器），用户ID啊什么的分服务器分区，建立服务器集群，对于每个集群，所有功能都是可用的（这样可以很好的做数据迁移以及冗余），使用一个hash结构或者主服务器来判断用户属于哪个服务器集群（^_^中心数据库）</p>
<p>第五步？估计很多应用都到不了这一步，已经分完善了，估计5000W左右都没问题。</p>
<p><strong>第六步：一点点痛苦</strong></p>
<p>扩展应用和数据库架构，提高性能，增加新功能，优化代码，访问还在增长，但是可控。</p>
<p>比较惬意的阶段了，1个亿的访问量轻松应对吧。</p>
<p><strong>第七步：进入未知的领域</strong></p>
<p>开始找系统的瓶颈了，电源、存储、CDN什么的，更多的数据中心，可能难题是夸地区的数据复制。</p>
<p>呵呵，这个应该是Google考虑的问题了.</p>
<p>想想我们的应用瓶颈在那里，不要一下子什么都想想全了，一步到位的做法是不存在的，知道那里不足，解决它，发现其他的不足，再解决，无休止的循环，直到你失业（什么问题都没有，还要你做什么啊）或者退休（该歇歇了）。</p>
]]></content:encoded>
			<wfw:commentRss>http://chaoqun.17348.com/2008/09/7-stages-scaling-of-web-apps/feed/</wfw:commentRss>
		</item>
		<item>
		<title>OpenSlopeOne: An Open Source Project implementing Slope One in PHP&#038;MySQL</title>
		<link>http://chaoqun.17348.com/2008/09/openslopeone-open-source-php-mysql/</link>
		<comments>http://chaoqun.17348.com/2008/09/openslopeone-open-source-php-mysql/#comments</comments>
		<pubDate>Fri, 12 Sep 2008 15:37:34 +0000</pubDate>
		<dc:creator>超群.com</dc:creator>
		
		<category><![CDATA[Data Mining]]></category>

		<category><![CDATA[Mysql]]></category>

		<category><![CDATA[Open Source]]></category>

		<category><![CDATA[PHP]]></category>

		<category><![CDATA[Collaborative filtering]]></category>

		<category><![CDATA[openslopeone]]></category>

		<category><![CDATA[slope one]]></category>

		<guid isPermaLink="false">http://chaoqun.17348.com/?p=68</guid>
		<description><![CDATA[About OpenSlopeOne
OpenSlopeOne is an implementation of Slope One based on PHP&#38;MySQL, it&#8217;s an open source project under GPL V3.
It aims to a fast way to use Slope One with PHP&#38;MySQL, and it can handle tons of data.
It&#8217;s localed on Google Code:
    http://code.google.com/p/openslopeone/
You can get the latest code here:
    svn checkout https://openslopeone.googlecode.com/svn/trunk/openslopeone
it uses Zend_Db as its [...]]]></description>
			<content:encoded><![CDATA[<p><strong>About OpenSlopeOne</strong></p>
<p>OpenSlopeOne is an implementation of Slope One based on PHP&amp;MySQL, it&#8217;s an open source project under GPL V3.</p>
<p>It aims to a fast way to use Slope One with PHP&amp;MySQL, and it can handle tons of data.</p>
<p>It&#8217;s localed on Google Code:</p>
<p>    <a href="http://code.google.com/p/openslopeone/" target="_blank">http://code.google.com/p/openslopeone/</a></p>
<p>You can get the latest code here:</p>
<p>    svn checkout https://openslopeone.googlecode.com/svn/trunk/openslopeone</p>
<p>it uses Zend_Db as its database layer.PHP5&amp;MySQL5 or higher.</p>
<p><strong>About Slope One</strong></p>
<p>Slope One is a family of algorithms used for <a href="http://en.wikipedia.org/wiki/Collaborative_filtering" target="_blank">Collaborative filtering</a> introduced in Slope One Predictors for Online Rating-Based Collaborative Filtering by <a href="http://www.daniel-lemire.com/fr/abstracts/SDM2005.html" target="_blank">Daniel Lemire</a> and Anna Maclachlan.</p>
<p>You can check <a href="http://en.wikipedia.org/wiki/Slope_One" target="_blank">http://en.wikipedia.org/wiki/Slope_One</a> to know more about Slope One.</p>
<p><strong>What&#8217;s the difference between OpenSlopeOne and Vogoo?</strong></p>
<p>As you know,there is also another implementation of Slope One based on PHP&amp;MySQL:<a href="http://sourceforge.net/projects/vogoo" target="_blank">Vogoo</a></p>
<p>What&#8217;s the difference?</p>
<p>OpenSlopeOne aims to a fast way to use Slope One with PHP&amp;MySQL, so it cares more about performance.</p>
<p>OpenSlopeOne has two modes to init the slope one table, one is based on pure PHP, the other is based on MySQL procedure, as you know, it will be much faster, and you can use it with any other programming language.</p>
<p>the bottleneck of Vogoo(read the source code of cronslope.php Line 70~Line 150, version 2.2) is to check whether the record exits or not. If there is tons of data, the performance is very bad.</p>
<p>In OpenSlopeOne, first I get distinct item ids, then foreach item id, i calculate the slope one of it. I do not have to check whether if it exits, and i am faster.</p>
<p><strong>Installation</strong></p>
<p>1. Modify the config ini file: config.ini.php</p>
<p>   ; &lt;?php exit; ?&gt; DO NOT REMOVE THIS LINE<br />
   [database]<br />
   host                 = localhost ; database host name or ip<br />
   username             = root      ; database user name<br />
   password             =           ; user password<br />
   dbname               =           ; database name<br />
   port                 = 3306      ; database host port,3306 as default<br />
   adapter              = PDO_MYSQL ; PDO_MYSQL or MYSQLI</p>
<p>2. Create user&#8217;s rating table:</p>
<p>    CREATE TABLE IF NOT EXISTS `oso_user_ratings` (<br />
      `user_id` int(11) NOT NULL,<br />
      `item_id` int(11) NOT NULL,<br />
      `rating` decimal(14,4) NOT NULL default &#8216;0.0000&#8242;,<br />
      KEY `item_id` (`item_id`),<br />
      KEY `user_id` (`user_id`,`item_id`)<br />
    ) ENGINE=MyISAM DEFAULT CHARSET=utf8;</p>
<p>there is a sample data file:sample.data, you can load it into the table</p>
<p>    load data infile &#8217;sample.data&#8217; into table oso_user_ratings;</p>
<p>3. Create slope one table:</p>
<p>    CREATE TABLE IF NOT EXISTS `oso_slope_one` (<br />
      `item_id1` int(11) NOT NULL,<br />
      `item_id2` int(11) NOT NULL,<br />
      `times` int(11) NOT NULL,<br />
      `rating` decimal(14,4) NOT NULL<br />
    ) ENGINE=MyISAM DEFAULT CHARSET=utf8;</p>
<p><strong>Usage<br />
</strong><br />
The main also the only PHP file is OpenSlopeOne.php, you must include it in your own PHP file:</p>
<p>    require &#8216;./OpenSlopeOne.php&#8217;;</p>
<p>    $openslopeone = new OpenSlopeOne();</p>
<p>1. Init the slope one table:</p>
<p>before you get the recommendtion, you must pre-computation the slope one table.</p>
<p>    $openslopeone-&gt;initSlopeOneTable($factory);</p>
<p>you can specify the mode use &#8216;PHP&#8217; or &#8216;MySQL&#8217;,If you user &#8216;PHP&#8217; mode, it&#8217;s a pure php implementation, and it might be very slow when there is tons of data.You can also use &#8216;MySQL&#8217; mode, it&#8217;s based on mysql procedure, and it will be mutch faster.</p>
<p>If you have tons of data to pre-computation, a good advice is that you do not index any colum on oso_slope_one before it done.</p>
<p>2. Get recommended items by item&#8217;s id</p>
<p>    $recommendedItems = $openslopeone-&gt;getRecommendedItemsById(9527);</p>
<p>3. Get recommended items by user&#8217;s id</p>
<p>    $recommendedItems = $openslopeone-&gt;getRecommendedItemsByUser(30002);<br />
<strong>About Author</strong></p>
<p>I am a PHP programmer in China, my blog:http://chaoqun.17348.com, mostly written in Chinese.</p>
<p>You can contack me with gtalk or mail:fuchaoqun@gmail.com</p>
<p>Welcom to any bug and advice.</p>
]]></content:encoded>
			<wfw:commentRss>http://chaoqun.17348.com/2008/09/openslopeone-open-source-php-mysql/feed/</wfw:commentRss>
		</item>
	</channel>
</rss>
