Archive for August, 2008

利用orange进行关联规则挖掘

Tuesday, August 26th, 2008

本博客所有原创文章采用知识共享署名-非商业性使用-相同方式共享,转载请保留链接http://chaoqun.17348.com/2008/08/data-mining-with-python-orange-association_rule/ 最近,趁着项目的间隙,折腾了一阵数据挖掘,在同事的帮助下,对新浪音乐用户的听歌记录进行了一个简易挖掘,希望能根据用户以往的听歌记录,推荐出用户可能感兴趣的其他歌曲。 Orange: 一个模块化的C++数据挖掘包,提供python接口(好像也只提供了python接口),网址是http://www.ailab.si/orange/ 关联分析: 我这里用的是类似购物篮分析,每个用户的听歌id是一个事务,不熟悉关联分析的同学可以去搜一些相关方面的资料。 数据准备: 简单清洗掉一些“脏”数据(逻辑上有问题的数据,比如某个用户在5s听了200首歌),得到类似下面的数据 15615,355029,750367,762147,803787,805014,999712,999712,999712,1013641,1024215,1028429 871029,952779,962769 1023040,1024077,1024215,1025600 757946,873801,873801,873801 862257,873479 286056,286056,286056,286056,286056,286056,286056,286056,286056,286056 873801,873801,873801,873801,873801,947750,947750 473221,473537,504206,504206,504206,504206,504206,504206 947750,1005430,1005430 974748,1024215 873479,873479,873801,873801,947750,965748,999721,1024215,1024215,1024215,1024215,1024215 873801,873801,873801 每一行是一个用户的听歌记录,没有做去重处理(orange示例中也没有,是不是可能会增加歌曲的权重?不清楚,没有去阅读orange代码),注意文件名一定要以.basket为扩展名,程序中文件地址是d:/datamining/sample.basket。 程序: # 导入orange包 import orange # 导入数据,注意不需要后缀 data = orange.ExampleTable("d:/datamining/sample") # 挖掘关联规则,输入最低支持度、最低置信度、最大项集数 rules = orange.AssociationRulesSparseInducer(data, support = 0.5, confidence = 0.6, maxItemSets = 1000000) # 打印出规则来 for r in rules: print "%5.3f %5.3f %s" % (r.support, r.confidence, r) 是不是非常的简单?Orange实现的是Apriori算法,由于Apriori算法的问题,一旦数据量非常大,你就等着你的内存消耗光吧,反正我这边要是把所有数据都导入进去的话,笔记本1.5G的的内存根本不够用,可以试试FP-tree算法,我这边参考文章利用sql改良构建fp-tree之技术,已经把fp-tree的前缀路径都找出来了,需要的朋友可以私下找我要,由fp前缀路径挖频繁集需要用到递归,用sql去处理就非常费劲了,所以后面的算法还需要自己去探索。 居于关联规则的挖掘就告一段落,因为算法的计算复杂度非常高,效果倒不是太好(因为对于音乐,用户可能听多遍,这样打分就不一样,可能用关联规则去挖电影类的数据比较好,因为电影一般最多就看一遍),现在研究的是协同过滤,如果不出意外的话,一个改良版的PHP+Mysql实现slope one算法过几天就要出来了,到时候我会开源出来的。

微软又丢人了

Friday, August 22nd, 2008

一大早上班,看到这则新闻微软照片共享网站上线首日陷入瘫痪 访问量过大,于是问微软的团队,你们怎么不看Flickr的架构啊,微软的团队回答说,我们没有Linux、我们没有Apache、我们没有PHP、我们没有Mysql,我们只有M(any)$。(YY,后面的话绝对是我YY的)。 看来还是有很多事情是钱办不到的。 很多公司在某个领域成功了,已经吃的很饱了,看到别人碗里还有吃的,于是乎就想“动人家的奶酪”,Microsoft看Yahoo火了于是也想做互联网,Yahoo看Google火了于是也想做搜索,中国人在这方面更强,看Youtuble火了,千万个Youtbule站起来了,看Myspace、Facebook火了,好多网站都变成蓝色的了。 恰逢北京奥运会,“伟大”的美国这次估计要“二”了,“伟大”的俄罗斯很多年前就已经是“小三”了,很多国家你别看小,金牌数少,可都有自己的看家绝活,看牙买加的速度,高丽棒子的准度,社会的分工已经由原来的大而全转化成专而精,可惜我们的互联网还在做着一统江湖的美梦。 Microsoft的hotmail,估计很多人只拿hotmail的邮箱做msn登录帐号,Google搜索已经做的很好了,可还是想做Gmail,我是Gmail的忠实用户,几乎每天碰到Gmail错误,难道像microsoft、google这样伟大的公司都做不好一个邮箱吗? 是的,他们不可能做好,他们的成功是由于他们的专注,有钱了就忘记筚路蓝缕的艰辛,记得还在出版社的时候,有次和google的员工在图书订货会上交流google图书馆计划,google的那个员工极其傲慢,oh,my god,google的成功和你一点关系都没有,如此傲慢的企业文化怎么可能把心态放低给用户创造好的产品? 整个世界都遵循着新陈代谢,micorsoft们,你们已经很成功了,为什么啥都要插一杆子呢?做好自己就完成了历史的使命,为什么还要做好别人呢? 我学生物的,但很反对保护大熊猫,既然它都不适应这个世界,为什么不能让它安静的走,还要用氧气一直让它苟延残喘? 没有永恒的美,除了怀念....

翻页保持checkbox勾选状态的实现

Wednesday, August 20th, 2008

本博客所有原创文章采用知识共享署名-非商业性使用-相同方式共享,转载请保留链接http://chaoqun.17348.com/2008/08/pager_with_checkbox_on/ 之前的一个项目,给新浪空间做的音乐控件,大家可以登录上去看一下,个人感觉产品设计不错,我现在天天就在上面在线听音乐,大家可以测试一下里面的搜索效果,在搜索结果的第一页选择一些歌曲(或者全选),然后翻任意页,再选,再翻任意页,再选,后头查一下你选择过了的页面,看看是不是你之前选择过的歌曲还保留着,下面就说一下这个的实现方式,javascript部分居于mootools。 实现原理: 每一个分页都包含在一个id为page+页码的div中,翻到其他页时,隐藏当前页,首先判断目标页是否已经加载过,如果没有就通过ajax去获取页面,同样包含在一个page+页面的div中,等于说所有的分页都在页面中,只不过非当前页的div都设为display=none了,所以细心的你可能会发现加载过的页面再加载怎么会那么快啊。 核心的部分就是这个javascript函数: // 显示分页,传进去分页页码 function show_search_page(page_num) { // 首先判断目标页是否已经加载过,加载过的话就把其他页都设定隐藏再把当前页显示,然后结束返回        if ($chk($('page'+page_num)))     {         $('song_div').getElements('div[id^=page]').each(function(item,index){$(item).setStyle('display','none');});         $('page'+page_num).setStyle('display','block');         return;     } // 如果没有加载过,就通过ajax去获取页面数据,然后生成一个page+页面的div,并显示出来     var search_song_with_ajax = new Ajax('./getsearchdata.php?cur_page=' + page_num + '&key={_$search_key_}',     {method: 'get',     onComplete: function(){         $('song_div').getElements('div[id^=page]').each(function(item,index){$(item).setStyle('display','none');});         var new_div = new Element('div',{             'id':'page'+page_num         });         $(new_div).setHTML(this.response.text);         $(new_div).injectInside('song_div');     }     }).request(); } javascript的代码可以到页面去查看,hope it usefull for you.

Zend Framework学习笔记(一):Zend_Controller

Wednesday, August 20th, 2008

本博客所有原创文章采用知识共享署名-非商业性使用-相同方式共享,转载请保留链接http://chaoqun.17348.com/2008/08/learn_zend_framework_zend_controller/ 前一段时间一直在看Zend Framework方面的东西,参照着设计模式阅读Zend Framework结构收获良多,我将以一系列的文章和大家一起探讨Zend Framework(文中将以ZF代替Zend Framework)。 ZF是Zend公司开发的一套PHP快速开发框架,类似的框架还有cakephp、codeigniter等,当然国内的也有一些这方面好的尝试,如FleaPHP&ThinkPHP,每个框架都有自己的特色,我这里只说一下ZF,ZF过于强大了以至很多人都觉得ZF是不是臃肿了?其实ZF在松散耦合方面做的非常的好,ZF的各个模块基本上都可以调出来单独使用,这就是很多人觉得ZF是PEAR2的原因。ZF的学习成本相对其他框架来说要高很多,当然也可以什么了解一点,然后写出只值“一点”的程序来,国内用ZF的大压力项目有6.cn,可惜Michael一直没有时间给我们分享更多,了解ZF可以关注一下phpeye,站长HaoHappy给我们贡献了ZF的中文手册,实在是一件功德无量的事情。 Zend Framework整个框架的核心的Zend_Controller,所以要学习ZF的同学最好把手册中Zend_Controller部分多看几遍,我们进入正题吧。 一、MVC的设计模式 一个良好的应用结构一般分为三层:Model层(一般是数据层)、View层(表现层)和Controller层(控制层或者说是逻辑层),底层数据和逻辑分离、逻辑与前台展现分离,这样的话程序的伸展性和可维护性都大大提高,下面的例子会将一个很糟糕的设计转化成一个PHP的MVC设计. 先看糟糕的程序吧: <?php $userID = 9527; /**  * 开始连接数据库,查询数据库等等的操作,类似:  * $db = mysql_connect('host', 'user', 'pwd');  * mysql_select_db('dbname');  */ // 假设最后你获得了用户名称 $userName = '超群.com' ?> <!-- 开始显示用户名称 --> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <title><?php echo $userName;?>,你好</title> </head> <body> Hell,<?php echo $userName;?> </body> </html> 我们会看到一个文件里面混合着PHP和HTML代码,如果美工修改了模板或者业务逻辑稍有变化都需要改大量的程序,而且还有一个致命的问题:代码复用,如果另外的地方也需要获得用户名称,你是不是要重新写一遍同样的代码呢?记住:重复代码是邪恶的。 然后再看一下MVC的设计,我们把业务划分为三块:获得用户名称(数据层)、控制用户名称显示(控制层)、用户名称显示模板(表现层): 数据层: 控制层: 表现层: <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <title>{$userName},你好</title> </head> <body> Hell,{$userName}> </body> </html> 这个例子非常简单,但确能给各位一个关于MVC在PHP的呈现结构。 二、Zend_Controller 一般的应用手册上面已经写的很详细了,建议仔细阅读手册,我这里想讨论的是不用mod_rewrite然后还想用传统的目录结构怎么处理?我想很多人的困在这里,手册上推荐的目录结构是: application/     controllers/         IndexController.php     ...

致年轻人的一封信

Tuesday, August 12th, 2008

  这是我今年年初从北师大出版社出来的时候写给我同事们的一封信,一晃我已经离开出版社快半年了,那里的很多事情还非常的清晰,毕竟在那里呆了近4年,在出版社工作回答了人生的第一个问题:我能做什么?初出校门,对社会的彷徨以及恐慌,都不知道自己能做什么,到出版社之后发现自己能做很多事情,有些还做的不错,我用近4年时间去回答了这个问题,然后出来了,因为要回答第二个问题:我要做什么?我会用这几年的时间去回答这个问题,希望下一个问题不要是我做了什么,因为做过的事情已经没有多大的意义了,尤其是自己感觉做的不错的事情。   首先向各位亲爱的朋友解释一下离职的原因,离职源于梦想,梦想就是做一个卑微的职业程序员,梦想打中学开始就有,在出版社工作的几年里虽也写了些程序,总觉不太成熟,在现在这个不大不小的年纪做出这个痛苦的决定,也是对职业和人生的一个重新规划。要走了,有些话想和我亲爱的朋友们说说。 生活的意义   生活是有一个支撑点,然后你的思维和活动都围绕它在转动,我亲爱的朋友们,有多久没有问过自己为什么而活着或者认为这个问题根本就是无稽的,我曾经迷失过我的支点,然后感觉整天忙忙碌碌的都不知道自己的忙什么,忙是因为有事情,事情来自两方面:别人要你忙的和自己想忙的,如果仅仅是忙于别人要你忙的,和拉磨的驴又有多大的区别呢?   有时候回过头想想自己所忙于的工作,收获可能比忙更多的工作来的更多。   你懂生活吗?我还不太懂,但我在试着和她沟通。 学习的意义   我们是一群优秀的年轻人,优秀是经过学校和出版社检验的,我们来的时候是优秀的,所以我们工作的时候应该也是优秀的,如果哪一天要走,那应该也是优秀的走。   我曾经困惑过,感觉自己像一块煤在出版社燃烧,担心成为灰烬的一天,我有些惶恐,想想哪天成为了出版社的寄生虫,那是多么可悲而且又可怕的一件事啊,出版社希望员工一直在出版社好好的干,可是她也不希望我们只能在出版社干,她希望我们一直保持我们优秀的品质。优秀不是我们进入出版社的敲门砖,而是我们赖以生存的根本,所以如果有一天出版社抛弃了我们,过错只在我们自己。   不断的学习是我们保持优秀的唯一法宝,我们在忙的同时有多少时间去学习呢?太忙曾经是我的借口,后来才发现安逸是我最大的敌人,鲁迅有句话,大意是这样的:物质生活太过安逸,工作和学习容易被生活所累,我想累及的不仅仅是工作和学习,累及的还有思想。   你学习了吗? 创新的意义   年轻意味着什么?犯错误,犯错误就是创新的开始,大家对我的了解可能仅限于计算机方面,很多人不知道我是学生物技术的,生物的进化只不过是环境对遗传犯的一个又一个错误的选择而已。   我们不是不想创新,我们是害怕犯错误,我们过早的接受了无过便是功的哲学。多么可悲的一件事情。我们进入出版社不仅仅是新陈代谢,而是带来一些不一样的东西,这也是出版社和我们所尊敬的老员工的期望。犯错误不是最可怕的,重要的过错之后的解决方法,我来出版社也犯了无数的错误,直至今日仍蒙几位老师错爱,这也是支撑着我人生信念的一个很重要因素。   创新就是思考的力量。 尊师的意义   我们很幸运的加入到出版社这个大家庭,出版社的今天是我们所尊敬的老同志们创造的,我们创造的是出版社的明天。   新老的交替和磨合难免会有些磕碰,我来也听过不少这方面的传闻。新同志汲汲的想担更重的担子,老同志拽着某些东西不放手,然后矛盾就产生了。   很亲爱的你分享一下我的做法,我来出版社应该也侵犯了不少人的利益,我做事情的原则有两点:我尊重他,发自内心的、我是为出版社利益着想的而不是为了取代他,很多人做好了第二点而没有做好的一点,我们的老员工是富于人情味的,和我们年轻人不同,这是我们缺乏并渴望得到的。   尊重一个人,然后才能把事情做好。 写在最后   我和不只一个人说过,我留出版社是因为舍不得这里的一些人,今天我要走了,唯一舍不得的还是那些人和新进入这个圈子的人。希望你们越来越好,希望出版社越来越好! 你们的朋友:付超群 写于2008年2月27日中午

让memcached和mysql更好的工作

Tuesday, August 12th, 2008

本博客所有原创文章采用知识共享署名-非商业性使用-相同方式共享,转载请保留链接http://chaoqun.17348.com/2008/08/memcached_work_with_mysql   这次是Fotolog的经验,传说中比Flickr更大的网站,Fotolog在21台服务器上部署了51个memcached实例,总计有254G缓存空间可用,缓存了多达175G的内容,这个数量比很多网站的数据库都要大的多,原文是A Bunch of Great Strategies for Using Memcached and MySQL Better Together,我这里还是选择性的翻译以及按照我的理解补充,感谢Todd Hoff,总能给我们一些学习的案例,从这里也能看出国外技术的开放态度,不似我们,其实就那么点小九九还藏着掖着,好了,进入正题。 一、关于memcached   还不知道这个?那你去面试的时候要吃亏了,赶紧去官方网站看一下http://www.danga.com/memcached/,另外google一下用法,硬盘总是太慢,把数据存在内存里面吧,如果你只有一台服务器,推荐用一下APC(Facebook在用)或者eaccelerator或者Xcache(国人开发的),这些产品单机效果更好,如果你需要分布式的缓存方案,那么用memcached吧。 二、memcached如何与mysql并肩作战? 通过数据库分片来解决数据库写扩展的问题把数据库分片,部署到不同的服务器上,免得只有一个主服务器,写操作成为瓶颈以及可能有的“单点故障”,一般的数据库分片主要是按照业务来分,尽可能的拆分业务,不相干的都独立起来做成服务也好 前端mysql和一堆memcached服务器来应付读的问题应用程序首先从memcached中获取数据,获取不到再从数据库中获得并保存在memcached中,以前看过一篇文章说好的应用95%的数据从memcache的中获得,3%的数据从mysql的query cache中获得,剩下2%才去查表,对比一下你的应用,差距有多远? 通过mysql复制(master-slave)来解决读的问题 首先mysql数据库通过master-slave读写分离,多个slave来应对应用程序读的操作。 三、为什么不用mysql的query cache?   我们都知道mysql有个query cache,可以缓存上次查询的结果,可实际上帮不上太多的忙,下面是mysql quety cache的不足: 只能有一个实例 意味着你能存储内容的上限就是你服务器的可用内存,一台服务器能有多少内存?你又能存多少呢? 只要有写操作,mysql的query cache就失效 只要数据库内容稍有改变,那怕改变的是其他行,mysql的query cache也会失效 mysql的query cache只能缓存数据库数据行 意味着其他内容都不行,比如数组,比如对象,而memcached理论上可以缓存任何内容,甚至文件^_^ 四、Fotolog的缓存技术 非确定性缓存你不确定你要的数据缓存中有没有,你也不知道是不是过期了,于是你就试探性的问memcached,我要的什么什么数据你那有吗?我可不要过期的数据啊,memcached告诉你说有并且给你,你就开心了,如果没有呢,你就要从数据库或者别的地方去获取了,这是memcached典型的应用。主要应用在: 1.复杂的数据需要多次读取,你的数据库做了分片处理,从多个数据库中获取数据并组合起来是一个非常大的开销,你大可以把这些数据取出来之后存到memcached中 2.mysql query cache的一个好的替代方案,这样数据库其他部门改变了,只要自己没改变就没问题(注意数据库更新的问题,后面会提到) 3.把关系或者列表缓存起来,比如某个栏目下的多篇文章列表 4.被多个页面调用并且获取起来很慢的数据,或者是更新很慢的数据,比如文章浏览排行榜 5.如果cache的开销超过重新获取的开销,那么不要缓存它吧 6.标签云和自动建议(类似google sugest) 例如:当一个用户上传一个图片,这个用户的好友页面上都要列出这张图片来,那么把它缓存起来吧。 潜在问题: memcached消耗的主要是服务器内存,对CPU消耗很小,所以Fotolog把memcached部署在他们的应用服务器上(貌似我们也是这样),他们遇到了CPU搞到90%的使用率(怎么会那么高?哪出问题了吧)、内存回收(这是个大问题)等等问题。 状态缓存把应用服务的当前状态存在memcached中主要应用在: 1.“昂贵”的操作,开销大的操作 2.sessions会话,Flickr把session存在数据库中,个人感觉还是存memcached比较“便宜”些,如果memecached服务器down掉了,那么重新登录吧。 3.记录用户在线信息(我们也是这样做的) 确定性缓存对于某些特定数据库的全部内容,都缓存到memcached,有一个专门的应用服务来保障你要的数据都在memcached中,其他应用服务直接从memcached中获取数据而不去取数据库,因为数据库已经全部保存到memcached中并保持同步。主要应用在: 1.读取伸展,所有的读取都从memcached中获得,数据库没有负载 2.”永不过期“(相对的)的数据,比如行政规划数据,变动很小吧 3.经常调用的内容 4.用户的认证信息 5.用户的概要信息 6.用户的参数设置 7.用户当前常用的媒体文件列表,比如用户的图片 8.用户登录,不走数据库,只走memcached(个人觉得这个不太好,登录信息还是需要持久化的,用类似BDB这样效果也不错) 使用方式: 1.多个专门的缓存池而不是一个大的缓存服务器,多个缓存池保障了高可用性,一个缓存实例挂掉了走其他的缓存实例,所有的缓存实例挂掉了,走数据库(估计数据库抗不住^_^) 2.所有的缓存池都用程序来维护,比如数据库有更新时,程序自动把更新后的内容同步到多个缓存实例中 3.服务器重启之后,缓存要比网站先启动,这就意味着当网站已经启动了,所有的缓存都可用 4.读取的请求可以负载均衡到多个缓存实例中去,高性能,高可靠性 潜在的问题: 1.你需要足够多的内存来存储那么多的数据 2.数据以行记录数据,而memcached以对象来存储数据,你的逻辑要把行列的数据转换成缓存对象 3.要维护多个缓存实例非常麻烦,Fotolog用Java/Hibernate,他们自己写了个客户端来轮询 4.管理多个缓存实例会增加应用程序的许多开销,但这些开销相对于多个缓存得到的好处来说算不了什么 主动缓存数据魔法般的出现在缓存中,当数据库中有更新的时候,缓存立马填充,更新的数据被调用的可能性更高(比如一篇新文章,看的的人当然多),是非确定性缓存的一种变形(原文是It's non-deterministic caching with a twist.我觉得这样翻译怪怪的)。主要应用在: 1.预填充缓存:让memcached尽可能的少调用mysql如果内容不展现的话。 2.“预热”缓存:当你需要跨数据中心复制的时候 使用步骤: 1.解析数据库更新的二进制日志,发现数据库更新时对memcached也进行同样的更新 2.执行用户自定义函数,设置触发器调用UDF更新,具体参考http://tangent.org/586/Memcached_Functions_for_MySQL.html 3.使用BLACKHOLE策略,传说中Facebook也用mysql的Blackhole存储引擎来填充缓存,写到Blackhole的数据复制到缓存中,Facebook用这来设置数据作废以及跨国界的复制,好处是数据库的复制不走mysql,这就意味着没有二进制日志以及对CPU使用不那么多(啊?难道通过memcached存储二进制日志,然后复制到不同的数据库?有经验的同志在这个话题上可以补充。) 文件系统缓存把文件直接缓存在memcached中,哇,够BT的,减轻NFS的负担,估计只缓存那些过于热门的图片吧。 部分页面内容缓存如果页面的某些部分获取起来非常费劲,以其缓存页面的原始数据还不如把页面的部分内容直接缓存起来直接调用 应用程序级别的复制通过API来更新缓存,API的执行细节如下:1.一个应用把数据写到某个缓存实例,这个缓存实例把内容复制到其他缓存实例(memcached同步) 2.自动获得缓存池地址以及实例个数 3.同时对多个缓存实例更新 4.如果某个缓存实例down掉了,跳到下一个实例,直到更新成功 整个过程非常高效以及低开销 其他技巧1.多节点以应对"单点故障"2.使用热备技术,当某个节点down掉了,另外一台服务自动替换成它的IP,这样客户端不用更新memcached的IP地址 3.memcached可以通过TCP/UDP访问,持续连接可以减轻负载,系统设计成可同时承受1000个连接 4.不同的应用服务,不同的缓存服务器群 5.检查一下你的数据大小是否匹配你分配的缓存,更多请参考http://download.tangent.org/talks/Memcached%20Study.pdf 6.不要考虑数据行缓存,缓存复杂的对象 7.不要在你的数据库服务器上跑memcached,两个都是吃内存的怪兽 8.不要被TCP延迟困扰,本地的TCP/IP对内存复制是做了优化的 9.尽可能的并行处理数据 10.并不是所有的memcached的客户端都是一样的,仔细研究你用的语言所对应的(好像php和memcached配合的不错) 11.尽可能的是数据过期而不是使数据无效,memcached可以设定过期时间 12.选择一个好的缓存标识key,比如更新的时候加上版本号 13.把版本号存储在memcached中   作者最后的感言我就不翻译了,貌似mysql proxy正在做一个项目,自动同步mysql以及memcached,更多参考http://jan.kneschke.de/2008/5/18/mysql-proxy-replicating-into-memcache   后记:前几天,把Flickr架构(一)这篇文章发在PHPChina那的原创板块,居然被管理员当成软文给删掉了,难道就因为我保留了版权信息?匪夷所思。

Flickr架构(二)

Wednesday, August 6th, 2008

本博客所有原创文章采用知识共享署名-非商业性使用-相同方式共享,转载请保留链接http://chaoqun.17348.com/2008/08/flickr_architecture_part_ii/ Flickr的架构不能说是完美的,没有完美的架构,ebay对于扩展有以下建议: 不要预先去为性能扩展,出现问题之后找到问题再寻扩展; 不要想寻找到一个一劳永逸的方案,因为你不知道下一个瓶颈在哪里; 访问量大了,出了问题,修改架构,稳定运行,访问量再大了,又出问题了,再修改,这个是解决问题的唯一方案。 Flickr是Lamp架构比较成功的案例之一,抛出Flickr的架构是因为看到国内很多的架构设计盲目、迷信以及短视,不过相对于架构来说,程序的结构更让人担忧,后面的我会写一些关于程序结构的文章,希望能和大家一起讨论成长,好了,我们继续Flickr的架构。 "Statelessness"设计,@fininan提示是“无状态”设计,比我开始译的达意许多,不过还是不太明白具体细节,原文"Statelessness means they can bounce people around servers and it's easier to make their APIs.",希望懂得朋友补充。 通过master-save的设计能解决一部分问题,但很快你就会发现不行了,常见的master-slave只能解决读的问题,但存在单点失败故障,而且当负载比较重的时候会存在复制延迟的问题,很多公司都会碰到。 搜索功能由专门的服务器群来支持,通过复制需要搜索的内容到搜索服务器去搜索,和App servers分开。 集群 1、分表:按照一定主键拆分数据表,比如按照用户划分; 2、一个用户的所有信息在同一组服务器上 3、数据能够在不同的服务器组上迁移(Statelessness) 4、一组中心服务器负责查询,比如定位某个用户在哪个服务器组 5、不要以用户ID作为分组的依据(Non-Statelessness) 服务器组中每台服务保持50%的负载,当某台服务器down机或者维护时,另外一台服务器100%负载 访问高峰时,每台服务器的负载可能高于50%,现在Flickr通过增加服务器来让服务器负载在50%以下 每个页面大概有27~35个mysql query(够高的),点击数统计、API调用数据库都是实时的(太恐怖了,估计只有Dathan Pattishall会这么变态的使用mysql) 每组服务器处理40万+的用户数据,很多数据存储了双份,比如A用户对B用户的博客进行了评论,那么评论的数据在A的数据表里面和B的数据表里面都存储了一份,Flickr通过事务来保证同步。(这样做的目的是保证同一个用户的数据都存放在同一组服务器上,省去复制的成本。) Flickr的硬件设置:Intel EMT64处理器/红帽RHEL4/16GB内存/6块15000转的硬盘做RAID-10/12TB用户数据(仅仅是数据库而不是图片)/2U服务器,每个服务大概有120GB数据 备份:每天不同时刻跑cron,每天晚上做数据库快照,专门的服务组来备份(和线上业务分开),交叉备份(比如每天、每周、每月) 每张图片都有自己的档案,档案包括大小、尺寸、像素等等,储存在数据中 每组服务器最多400个连接,45个线程缓存 Tag标签,Flickr发现常见的数据库结构不能很好的处理巨大的标签库,他们采用的是类似倒排索引以及大量的缓存来处理,并且Tag标签不是实时的,他们会在线下进行统计 Flickr目标是所有的事务都做成实时的,没有延迟(个人觉得倒是没有这个必要) Todd Hoff总结的经验: 不要把你的应用简单的看成一个Web应用,可能会有REST APIs, SOAP APIs, RSS feeds, Atom feeds等等的应用 无“状态”设计,不要把你的用户死死的绑定在某个服务器上 产品设计时需要做扩容的计划以及预算 慢慢来,不要一开始就买一堆服务器 实地考察,不要臆想,获得实际数据之后再做决定 内建日志系统,记录服务器和应用日志 Cache,缓存是必不可少的 抽象层,由于你的架构随时可能变,架构的变化必定要带来底层的变化,这就需要你在底层的基础上根据业务封装一层中间层,这样底层的改动不至于影响业务(这个太重要了,不要因为扩展把原来的程序推倒重来) 迭代开发,随时改进 忘记那些调优的小技巧吧,比如很多人对与PHP里面的require和require_once的性能差别,这些性能的差异和架构上的短板比起来根本不足为道 在线上测试你的效果 忘记用工具测试出来的结果,这些结果只能给你一个大概的印象而已 找出你的系统短板,一台服务器的最大处理能力是多少?现在离最大负载还有多远?mysql的瓶颈在哪里?是不是磁盘IO?memcache的瓶颈在哪里?CPU还是网络传输? 注意你的用户使用规律,比如Flickr发现每年的第一个工作日比平时多20%~40%的上传量,周日的访问量比平时要多40%~50% 要注意指数型的增长 你的计划是为你访问的峰值设计的 补充:阅读完原文的评论,有一个评论翻译出来给大家分享: Flickr如何存储图片的呢? 标准的Flickr图片Url是这样的http://farm1.static.flickr.com/104/301293250_dc284905d0_m.jpg,其中farm1是Flickr的服务器群,static.flickr.com是Flickr静态图片服务器,104是服务器ID,301293250是图片ID,dc284905d0是Flickr的加密串,防止盗链,m表示图片的尺寸。m表示中等尺寸 后记: 终于“翻译”(姑且用这个词)完了,看到原文的一个评论是"Hmm... i can not beleive flickr written on php...",借用好像也是Flickr的人说的一句话:扩展的不是语言,而是架构。国内很多大的企业都在用PHP(比如我所在的sina),PHP总给人是草根语言的感觉,是因为没有人肯分享自己的架构,以及程序员写程序的时候不注意自己的结构(设计模式),好的架构只能让你的程序跑的更快,好的结构让你的程序更易于维护,更容易让别人看的懂,更容易团队合作。

Flickr架构(一)

Monday, August 4th, 2008

本博客所有原创文章采用知识共享署名-非商业性使用-相同方式共享,转载请保留链接http://chaoqun.17348.com/2008/08/flickr_architecture_part_i/ Flickr(http://www.flickr.com/)是国外一个领先的图片分享网站,现在应该在yahoo门下,感觉yahoo还是有很多好东西,奈何资本要抛弃他了。这个轮回其实挺有意思的,起先是做实业被microsoft郁闷了,说软件是虚的值不能那么多钱,然后microsoft被yahoo郁闷了,说互联网是虚的不值那么多钱,然后是yahoo被google郁闷了,yahoo比较厚道没说什么,现在microsoft要收购yahoo了(折腾好久了,估计要落听了吧),不知道google将来要被谁郁闷了。成功建立在相同的失败上,反过来失败都是建立在相同的成功上也成立,进入正题吧。 原文地址是http://highscalability.com/flickr-architecture,本文不是原文的严谨翻译,带有我的理解以及补充,由于水平有限,文中的错误请各位斧正。 Flickr处理的数据: 多达40亿次的请求(http request or database query?不知道了,不管是哪个,都够大的吧。) squid总计约有3500万张图片(硬盘+内存) squid内存中约有200万张图片 总计有大约4亿7000万张图片,每张图片大概4~5MB 每秒3,8000次请求 (存储了1200万对象在里面) 2 PB 存储(星期天要消费~1.5TB) 每天新增图片超过 400,000 吓死人不偿命.... Flickr用到的技术: PHP MySQL Shards Memcached 作为中间缓存层,memcached在web2.0网站中可能是引用最广泛的产品之一,开源&强大. Squid 作反向代理服务器(reverse-proxy for html and images). Linux (RedHat),如果你想用RedHat企业版又不想付费,试试这个CentOS,基本上100%克隆RedHat企业版(估计传说中1%的RedHat代码没有),我用的就是这个。 Smarty 作为模板解析,很多人在讨论smarty这不好那不好,但是大网站都在用,稳定而且功能强大,系统的瓶颈从来不会再smarty这里,我保证。 Perl 估计用perl做一些系统层面的东西吧,比如日志处理(猜测) PEAR 做XML和Email解析,和我们一样,Flickr用的也是PHP4,不过新项目还是用PHP5吧 ImageMagick 图像处理的不二选择 Java, for the node service,Java就不太了解了,希望读者补充 Apache 大家都在用,尝鲜的用户nginx或者lighttpd(适合静态文件,youtube用它做媒体文件服务器),出了问题你会抓狂的。 SystemImager 作为服务器部署 Ganglia 分布式系统监控,或者你可以试试nagios,据我所知也很多公司在用 Subcon 用SVN维护服务器配置文件并且可以部署不同的配置文件到服务器集群中去(这个我没用过,系统运维的可能会喜欢) Cvsup 文件分发,是否类似rsync? Wackamole前端负载均衡,类似的产品有http://haproxy.1wt.eu/ Flickr架构 常见的Squid反向代理、PHP App Servers、Net App's、Storage Manager我在这里就不讲,我们关注一些让人兴奋的特征: Mysql的Master-Master结构,mysql的常见的master-slave结构,大家都知道存在"single point of failure"(单点故障的问题),且只对读操作有好处,对于写频繁的网站却不是一个好的解决方案,Flickr的双master方案据我推测用的就是这个http://code.google.com/p/mysql-master-master/,原理就是master轮询,保证同时只有一个master负责写,解决了单点故障的问题。  Dual Tree Structure,看看下面的图就知道什么是“双树结构”(姑且这么翻译) 示例图中上方的2台机器为master,下方的4台为slave,这种“双树结构”的设计保证一个slave只有一台master,易于扩展也不会形成环路。原文中说这种设计是1+1=200%的设计,简单高效。为了防止自增长冲突,数据表中无自增长列。 补充:对于大型应用的分表设计,防止自增长冲突是个问题,有个简单的方案:比如分3张表,可以设第一张表从1开始以3跳跃递增,那么第一张表存储的序列为1,4,7,10……,第二张表从2开始也以3跳跃递增,第二张表存储的序列为2,5,8,11……,第三张表从3开始以3跳跃递增,第三张表存储的序列为3,6,9,12……,保证不会有重复的序号,但这种方案的缺点是如果数据爆炸,3张表不够,你分4张表呢?需要手工迁移数据,如果程序写的不好,底层又要大动了。 Flickr采用的方案是一个中心'users' table(用户表),记录的信息是用户主键以及此用户对以的数据库片区(有点类似Key->Value的设计,这样的数据结构查询起来是非常迅速的,据说Google的用户登录数据用的就是这样的设计,通过改进版的BDB数据库存储用户名和密码,这样登录起来就不用去查那个大表了),从中心用户表中查出用户数据所在位置,然后直接从目标位置中取出数据。 用专门的服务器存储静态内容,这个容易做到,比如专门的图片服务器。 "Use a share nothing architecture"这个比较费解了,字面上的意思是使用一个无共享的架构,其实就是解除架构上的依赖,类似我们写程序解耦合一样。 除图片外,所有的数据都存在数据库中,这里他们提到了PHP session,我们知道php的session是存储在服务器文件系统的,而且默认没有做hash目录,这就意味着如果你的网站访问量大,比如有10万个人在线,你的session目录下就有10万个文件,如果你的文件格式是NTFS(windows)或者Ext3(Linux),你要定位到某个文件,系统基本上会假死,有个好的建议是不要在一个文件夹下放超过1000个文件。使用默认的php session还有另外一个问题:服务器session同步,用户在A服务器登录后,session存储在A服务器上,然后应用跳转到B服务器,B服务器上的session没有同步就出问题了,当然解决这个问题的方法很多,比如统一存储session,所有服务器的session都存储在一个vfs设备上,或者通过cookie重新生成一个session在B服务器上 写一篇这样的文章非常的辛苦,第一部分就先写到这里,待续……

网站日志统计分析技术

Friday, August 1st, 2008

本博客所有原创文章采用知识共享署名-非商业性使用-相同方式共享,转载请保留链接http://chaoqun.17348.com/2008/08/web_log_analytics/ 前段时间,一直关注一个开源的统计系统http://www.piwik.org,有两个方面的原因,一是对统计非常有兴趣,二是这个项目构建在Zend Framework上,实在是学习ZF难得的一个样板,程序写的很优美,建议想学ZF的人可以阅读piwik源代码,好的,切入正题。 一般的网站统计系统我知道的有三种类型: 分析网站日子(Apache log),代表的有 AWStats:http://awstats.sourceforge.net/ analog:http://www.analog.cx/ Webalizer :http://www.mrunix.net/webalizer/ 使用Apache log好处是高速、低开销(apache随着访问写log开销不大),但是功能一般,比如不能记录客户端的的分辨率、色深、是否支持Java或者Flash等。 通过在网页中嵌入一个探针记录网站日志,piwik就是这样的典型,特点是快速、高开销(piwik是通过php直接往mysql中写记录)、功能强劲(可以记录客户端的详细信息)。 混合使用,代表是Google Analytics、缔元信,这些产品也是在网站中加入一个探针,然后发送一个类似http://www.google-analytics.com/__utm.gif?******的请求,其实是访问服务器上一个空白的图片文件,目的只有一个,记录http日志,这种方式的好处是集上面两种的好处:效率高、功能强。 探针的Javascript代码我们就不分析了,看一下Google Analytics最后发送的请求是什么样子的: http://www.google-analytics.com/__utm.gif ?utmwv=4.1 //常量 &utmn=1048672819 //随机数,还记得我是怎样处理mootools ajax问题吗? &utmhn=code.google.com //网站 &utmcs=UTF-8 //编码 &utmsr=1280x800 //分辨率 &utmsc=16-bit //色深 &utmul=zh-cn //语言 &utmje=1 //是否允许Java &utmfl=9.0%20%20r115 //9.0 r115,Flash播放器版本 &utmdt=Google%20Code //Google Code,标题 &utmhid=594665987 &utmr=- //来源地址 &utmp=/ //目录 &utmac=UA-18071-1 //帐号 &utmcc=__utma%3D247248150.137016230.1209822741.1209822741.1209822741.1%3B%2B__utmz%3D247248150.1209822741.1.1.utmcsr%3D(direct)%7Cutmccn%3D(direct)%7Cutmcmd%3D(none)%3B // 一些加密字串 有兴趣的可以去读一下ga.js源码,入门的话看一下yahoo的统计源码,写的比较清晰,也没有作混淆。

KISS with you

Friday, August 1st, 2008

本博客所有原创文章采用知识共享署名-非商业性使用-相同方式共享,转载请保留链接http://chaoqun.17348.com/2008/08/kiss-with-you/ 看到一个同事邮件里面提到的项目困扰,结合过去一个失败案例,写的一段文字供产品人员和程序员参考以应对项目复杂性。 keep it simple,stupid! 武侠小说里面,招式唯快不破,田伯光的快刀碰见令狐冲郁闷了,独孤九剑以无招胜有招。没有招式敌人再快也无用。 招式唯快就是性能至上主义,以前见过一些天书般的代码(代码足够长、足够复杂、足够你细细品味也看不懂),然后自我得到“极大的满足”,就算有“极好”的性能(好不好谁也不知道),出了bug或者升级的时候就抓狂了,自己都理不清楚了。 招式唯快就是功能至上主义,客户核心的需求就是那20%的功能,然后进入一个天花乱坠的页面,还有“强迫需求”,然后郁闷了,跑了。 简单是世间最美的事情,简单首先保证不会有错误,简单意味着更加易用以及易于维护。 有两条建议与大家分享: 第一条:keep it short,sweety. 过长的代码不容易理解(看了后面忘记前面),很多人说如果一个函数的行数超过70行,一个文件代码超过500行,这个函数,这个文件就太长了,怎么办?看第二条。 第二条:keep it separated,sweety. 过长的代码,把他们分开吧。 什么时候需要分离代码?有重复代码(重复是邪恶的)、一个函数超过70行、一个类有数十个成员函数或方法,把他们拆开吧。 什么地方需要分离代码?martin fowler的建议是如果你在程序里面有需要的注释的时候,就想想能不能拆开,用一个函数去取代。 分离的代码更好理解,想想你要看一个30行的程序,看到最后才知道是获取用户信息的操作,如果把这30行程序拆出来新建一个函数getUserInfo(),噢,整个世界都清净了。 分离的代码更好维护,因为分的足够细,哪个环节需要都可以做局部的修改而不至于影响整个系统。 对于产品,求你了,不要把我用不到东西呈现给我,我想用的时候我可以点开。