Archive for February, 2009

PHP SESSION解惑

Saturday, February 28th, 2009

一、PHP SESSION原理 我们知道,session是在服务器端保持用户会话数据的一种方法,对应的cookie是在客户端保持用户数据。HTTP协议是一种无状态协议,服务器响应完之后就失去了与浏览器的联系,最早,Netscape将cookie引入浏览器,使得数据可以客户端跨页面交换,那么服务器是如何记住众多用户的会话数据呢? 首先要将客户端和服务器端建立一一联系,每个客户端都得有一个唯一标识,这样服务器才能识别出来。建议唯一标识的方法有两种:cookie或者通过GET方式指定。默认配置的PHP使用session的时会建立一个名叫"PHPSESSID"的cookie(可以通过php.ini修改session.name值指定),如果客户端禁用cookie,你也可以指定通过GET方式把session id传到服务器(修改php.ini中session.use_trans_sid等参数)。 我们查看服务器端session.save_path目录会发现很多类似sess_vv9lpgf0nmkurgvkba1vbvj915这样的文件,这个其实就是session id "vv9lpgf0nmkurgvkba1vbvj915"对应的数据。 真相就在这里,服务器将session id传递到服务器,服务器根据session id找到对应的文件,读取的时候对文件内容进行反序列化就得到session的值,保存的时候先序列化再写入。 事实就是这样,所以如果服务器不支持session或者你想自定义session,完全可以DIY,通过PHP的uniqid生成永不重复的session id,然后找个地方存储session的内容即可,你也可以学flickr把session存储在MySQL数据库中。 二、使用session之前为什么必须先执行session_start()? 了解的原理之后,所谓的session其实就是客户端一个session id服务器端一个session file,新建session之前执行session_start()是告诉服务器要种一个cookie以及准备好session文件,要不然你的session内容怎么存;读取session之前执行session_start()是告诉服务器,赶紧根据session id把session文件反序列化。 只有一个session函数可以在session_start()之前执行,session_nam():读取或指定session名称(比如默认的就是"PHPSESSID"),这个当然要在session_start之前执行。 三、session影响系统性能 session在大访问量网站上确实影响系统性能,影响性能的原因之一由文件系统设计造成,在同一个目录下超过10000个文件时,文件的定位将非常耗时,PHP支持session目录hash,我们可以通过修改php.ini中session.save_path = "2;/path/to/session/dir",那么session将存储在两级子目录中(修订:参见david回复),不过好像PHP session不支持创建目录,你需要事先把那么些目录创建好 。 还有一个问题就是小文件的效率问题,一般我们的session数据都不会太大(1~2K),如果有大量这样1~2K的文件在磁盘上,IO效率肯定会很差,PHP手册上建议使用Reiserfs文件系统,不过Reiserfs的前景堪忧,Reiserfs的作者把媳妇给杀了,SuSE也抛弃了Reiserfs。 其实还有很多中存储session的方式,可以通过php -i|grep "Registered save handlers"查看,比如Registered save handlers => files user sqlite eaccelerator可以通过文件、用户、sqlite、eaccelerator来存,如果服务器装了memcached,还有会mmcache的选项。当然还有很多,比如MySQL、PostgreSQL等等。都是不错的选择。 四、session的同步 我们前端可能有很多台服务器,用户在A服务器上登录了,种下了session信息,然后访问网站的某些页面没准跳到B服务器上去了,如果这个时候B服务器上没有session信息又没有做特殊处理,可能就会出问题了。 session同步有很多种,如果你是存储在memcached或者MySQL中,那就很容易了,指定到同样的位置即可,如果是文件形式的,你可以用NFS统一存储。 还有一种方式是通过加密的cookie来实现,用户在A服务器上登录成功,在用户的浏览器上种上一个加密的cookie,当用户访问B服务器时,检查有无session,如果有当然没问题,如果没有,就去检验cookie是否有效,cookie有效的话就在B服务器上重建session。这种方法其实很有用,如果网站有很多个子频道,服务器也不在一个机房,session没办法同步又想做统一登录那就太有用了。 当然还有一种方法就是在负载均衡那一层保持会话,把访问者帮定在某个服务器上,他的所有访问都在那个服务器上就不需要session同步了,这些都是运维层面的东西。 就说这么多吧,根据自己的应用来选择使用session,不要因为大家都说session影响系统性能就畏首畏尾,知道问题,解决问题才是关键,惹不起躲得起不适合这里。

改善MySQL上16进制标识符性能的5种方法

Friday, February 13th, 2009

前言: 原文地址:5 ways to make hexadecimal identifiers perform better on MySQL,作者是《High Performance MySQL, Second Edition.》的主要作者。这里是原文的节译。 在我们的业务中,经常会用md5()或者uuid()作为作为数据的标识,比如B2C网站,经常会有一些电子优惠券,常用就是通过md5()获得一个32位字符串,给字符串建立索引,然后用户输入优惠券代码之后系统查出此优惠券对应的折扣率:select discount from coupon where id = '0cc175b9c0f1b6a831c399e269779527',这样的做法存在两个问题:数据和索引很大以及非顺序数据,本文忽略“非顺序数据”的问题,因为顺序数据和非顺序数据的优劣很大程度上由技术特性决定。这里讲得是如何在使用16进制大数据的情况下保持好的性能,主要讲的是MySQL数据库,对其他数据库应该也起作用。 一、小心你的字符编码 看一下下面这个SQL语句: mysql> explain select * from t where id = '0cc175b9c0f1b6a831c399e269772661'\G *************************** 1. row *************************** id: 1 select_type: SIMPLE table: t type: const possible_keys: PRIMARY key: PRIMARY key_len: 98 ref: const rows: 1 Extra: Using index 为什么索引是98byte?简单,因为我们用的是UTF-8: CREATE TABLE `t` ( `id` varchar(32) NOT NULL, PRIMARY ...

扩展Digg和其他的网络应用

Thursday, February 12th, 2009

前言: 关于Digg的架构,之前Fenng已经写过一篇Digg 网站架构的文章,Fenng在文章开头说“越来越发现其实都是自我重复的劳动,后续的信息都是嚼别人剩下的甘蔗。”,其实是Fenng过谦了,我就是从DBA notes一点一滴开始学习的。 原文在Scaling Digg and Other Web Applications,Todd Hoff总能给我们带来一些有意思的文章。这里既不直译也不全译,保持原文骨干加上肤浅的理解。 Digg用户在3000W左右,网站每秒请求数达到13000个,规模算是很大了,Lamp(Linux+Apache+MySQL+PHP)结构,Web2.0网站中钟情PHP的不少,国外的Facebook、Digg、Flickr...,国内的新浪博客、开心网、51.com等等,扩展的不是编程语言而是网站架构,因为编程语言从来都不会是网站瓶颈,每种语言都有自己适合做和不适合做的事情。喜欢比较语言快慢的可以看看http://shootout.alioth.debian.org/,看看而已不要用速度去衡量编程语言的优劣。 Digg的扩展战略: 扩展是有特殊性的,当已有的方案不能满足需求时,你需要根据自己的特殊需求来建立方案。要熟悉自己的业务需求,找出系统的短板,解决问题,避免过度优化。 编程语言不需要扩展,因为语言从来都不是瓶颈,把PHP提速300%不会是什么大问题。 去中心化,分布式处理高并发请求。 水平扩展,用更多更便宜的机器代替更高效更昂贵的机器。 数据库驱动的网站需要作水平分区和垂直分区扩展,水平分区就是把数据放到不同的机器上,比如按照时间划分:2009年的数据一组服务器,2009年的数据一组服务器,或者按照用户ID划分:每200W用户一组服务器;垂直分区把一个表分成多个表,每个表只包含一部分字段,可以按照业务分或者常用的“动静分离”,比如对文章计数的字段大可以和文章内容字段分开。 建立数据应用层,程序不要直接操作数据库分区,这样可以保持好的扩展性。这一点非常重要,如果没有数据应用层API,一旦对数据库分区做了修改,你的程序就需要大改了。程序的稳定性和持续性就不能保证。比较常见的MVC开发模式,数据层和逻辑层分开,一旦数据库底层改变了,只需要对数据层做简单的修改,不影响既有业务。 一致性、高可用性、分区容忍性(Partition Tolerance)三者只能取其二,具体的阅读http://camelcase.blogspot.com/2007/08/cap-theorem.html,一致性:每个人看到的都一样,哪怕当时就有更新;高可用性:部分故障不影响使用;分区容忍性:数据分区还能保持系统所有属性。 数据分区要求反常规化,这是在Digg是大问题,同样的数据要复制到多个对象上去,而且还好保持同步。 采用异步的队列架构,把任务放到队列中交由多台机器处理,而不是一次同步处理,关于异步任务处理可以阅读 Flickr - Do the Essential Work Up-front and Queue the Rest和The Canonical Cloud Architecture icons和图片采用MogileFS存储,MogileFS是一个分布式的文件系统,由memcached作者开发,国内的好看簿也有使用MogileFS。 采用APC作为PHP加速器,Facebook用的也是这个,PHP5.30以后将自带APC加速器,同样的产品还有eAccelerator和Xcache,国内可能用eAccelerator的比较多,windows的版本我一般用这个网站编译的 缓存策略:永久性缓存和失效期缓存;基本不更新的内容采用文件缓存;动态缓存采用memcached;极少修改的数据采用APC缓存,APC不是分布式缓存,直接缓存的机器内存中,所以速度更快,适合缓存那些配置文件,比如MySQL的配置信息;缓存采用链责任模式,首先看PHP全局变量,如果没有就查APC,再没有就查memcached,最后没有就查数据库,这个觉得不太必要,你的缓存存在哪来你自己还不清楚阿。 Digg的推荐引擎是一个自定义的图形数据库,数据库最终保证一致性,先写如一个分区,然后再逐步写到其他的分区。在更新的过程中,可能取出来的数据不一致,是因为有不同的数据库分区处理,最终将会是一致。 MemcacheDB:性能革命性的一步 想象一下Kevin Rose,Digg的创建者,当前有4000个追随者,如果Kevin Rose一天digg一次,将会有4000个写操作,最热门的digg有最多的追随者,这会导致巨大的性能瓶颈: 你不能同时更新4000个追随者的帐号信息,幸运的是我们可以通过前面提到的异步队列方式解决。 Digg面临的海量写操作,如果平均每个人有100个追随者,一天就有3亿条写操作,平均到每秒有3000个写操作,每天有5GB的数据存储,在50~60台服务器之间有5TB的数据交换。 如果是MySQL的话估计早就挂了,Digg在笔记本上的测试是memcachedb每秒可以处理15000个写操作,memcachedb的测试结果表明每秒可以支持23000个写操作或者64000个读操作,足以应付Digg洪水般的请求。 memcachedb是一个分布式的kye-value型数据持久化解决方案,提供兼容memcached协议接口,由Sina工程师Steve Chu开发。 提到Digg为什么采用memcachedb,Digg的工程师给出的理由是:需要持久化缓存数据,与memcached协议兼容,Digg已经有很多业务应用了memcached,可以很方便的切换到memcachedb上去,开源。 后记: 很高兴的看到除了Sina,memcachedb又有一个超重量级的应用了,性能和可靠性都得到了极大的检验,大可放心应用生产环境中。我还说今年有空写个玩具版的类memcachedb项目,还是不要了,有时间还是去做些更有意义的事情,无谓重复发明。

基于Infobright的MySQL数据仓库方案测试

Tuesday, February 10th, 2009

数据仓库之父Bill Inmon在1991年出版的“Building the Data Warehouse”一书中所提出的定义被广泛接受——数据仓库(Data Warehouse)是一个面向主题的(Subject Oriented)、集成的(Integrated)、相对稳定的(Non-Volatile)、反映历史变化(Time Variant)的数据集合,用于支持管理决策(Decision Making Support)。 上面这段是抄的,简单的讲数据仓库是一个面向主题的、集成的、不可更新的、随时间不断变化的数据集合,它用于支持企业或组织的决策分析处理,这一句也是抄的,更直白的举例就是公司的历史交易数据集合,网站的历史访问数据集合,这一句是原创的。 Infobright是开源的MySQL数据仓库解决方案,引入了列存储方案,高强度的数据压缩,优化的统计计算(类似sum/avg/group by之类),下面是Infobright的架构图: 安装篇: Infobright目前还不支持windows系列操作系统,不过你可以装在Linux虚拟机上,或者从官方直接下载做好的VMWARE虚拟机,这里的测试环境是CentOS 5.2 32bit操作系统。Infobright也不支持以插件的形式集成到已有的MySQL系统中去,官方的说法是对MySQL做了很多修改,不支持以插件的形式使用。 详细的安装参照http://www.infobright.org/wiki/Install_Guide/,需要注意的是如果你的系统中已经有MySQL(默认端口3306),你需要重新设定一下安装参数,比如像我的: ./install-infobright.sh --datadir=/data/infobright/data --cachedir=/data/infobright/cache --port=9527 --config=/etc/my-ib.cnf --socket=/tmp/mysql-ib.sock --user=mysql --group=mysql 配置文件包括MySQL配置文件(/etc/my-ib.cnf)和Infobright配置文件(在安装时候指定的数据目录内,比如/data/infobright/data/brighthouse.ini),如果需要支持MySQL原生的SQL查询,需要修改brighthouse.ini设定 AllowMySQLQueryPath = 1 这样不至于有些SQL语句不能执行,安装完成,启动服务: /etc/init.d/mysqld-ib start 通过命令行:mysql-ib -uroot直接连接,默认密码为空,修改密码: /usr/local/infobright/bin/mysqladmin  -u root -p password NEWPASSWORD 测试篇: 测试数据是一个1.5GB大的文本数据,数据格式类似: 用户ID  内容ID  用户打分 765331  3868    5 716091  3868    3 1663216 3868    3 51971   3868    5 在测试数据库中新建两张表,一个为Infobright支持的brighthouse存储引擎,一个为MySQL原生的MyISAM存储引擎,其他内容一致: CREATE TABLE `t_ib` ( `uid` mediumint(9) NOT NULL, `cid` smallint(6) NOT NULL, `rating` tinyint(4) NOT ...

Smarty到底怎么了?

Thursday, February 5th, 2009

早期的PHP开发者发现把PHP代码和HTML代码混在一起,维护起来非常的费劲,于是Smarty应运而生,将PHP代码和HTML代码分离,把MVC模式中的V(View显示)首先剥离,大大提高了程序的可阅读性和可维护性,于是有一大批的使用者,甚至到现在,如果你去找一份PHP的工作,如果不了解Smarty,估计够呛能拿到offer. Smarty,这个PHP模板引擎曾经的代名词,最近的日子貌似不太好过,3.0Alpha出来了,不过很多人认为换汤不换药,更有甚者用回光返照来形容,真是英雄末路。 国外甚至有个No Smarty的网站,数典Smarty的种种"罪行",然后给出了很多种替代方案(基本上是主流的PHP框架),个人觉得不太公平,一个PHP模板引擎和一个完备的MVC框架能有可比性? 一个纯粹的PHP模板引擎在当前MVC当道的年代,难免不那么叫好,而且Smarty开发年代久远,有些设计确实不是太好,Smarty最早开发的时候估计是给页面制作人员(builder)来用的,以至于它的语法都和PHP不兼容,比如foreach语句,在Smarty里面就要用{foreach from=$foo item=bar},可是套模板的工作基本上都是PHP程序员在做,不兼容PHP语法还需要重新去学习Smarty,而两种不同的风格足够让人抓狂。 如果是因为效率的问题放弃使用Smarty,大可不必,很多的网站还在用,比如我们,瓶颈不会出现在Smarty模板上,Smarty会把模板代码编译成PHP代码,下次调用的时候检测如果模板没有改动直接调用编译好的PHP代码,应该和原生的PHP代码差别不太大。而且Smarty自带了很多很有用的函数,比如转义,用起来还是很舒服的。 不过,最近Smarty项目已经从PHP官方移除了,访问http://smarty.php.net,提示Smarty is no longer a subproject of the PHP project, and has subsequently moved to its own domain: www.smarty.net,真是祸不单行。

基于Slope One的相关歌曲推荐算法

Tuesday, February 3rd, 2009

本博客所有原创文章采用知识共享署名-非商业性使用-相同方式共享,转载请保留链接http://chaoqun.17348.com/2009/02/slope-one-for-music-recommender-system/ 不知不觉,研究歌曲相关推荐快半年了,第一篇文章利用orange进行关联规则挖掘完成于2008.08.26,到现在基本搞定基于矩阵奇异值分解(SVD)的协同过滤算法,期间得到了很多朋友的帮助,在此致谢。有些收获,将逐步的分享出来,有兴趣的可以参照研习。 对于Slope One算法,不熟悉的可以参照我之前的文章:Slope one:简单高效的推荐算法,已经被很多人证明有很好的推荐效果。 Slope one算法中有一个很重要的步骤是获取用户的打分数据,这个对很多网站都很费劲,很多用户都会听歌,但大多懒得去给歌曲打分,另外用户打分的时候会比较困惑,该打多少分呢?喜欢这首歌,是打4分还是5分呢?费劲。 我这里给出的是另外一种方法,做法是分析用户的听歌记录,一般网站都会记录这样的记录,统计一段时间内用户的听歌记录,我们得到下面格式的数据: 用户ID    歌曲ID    听歌次数 比如某个片段: 3389    9527    23 3389    9528    56 3306    1211    78 3306    9527    45 表示用户3389听歌曲9527的次数是23,听9528的次数是56,诸如此类。这样的数据当然不能直接用来做Slope one,需要把数据格式化到某个区间。我们分析一下用户听歌的行为,一般来说最喜欢的歌曲听的最多,越喜欢的歌曲听的越多,听的少的歌曲自然不那么喜欢。所以我们可以简单的模拟用户对歌曲的打分: 用户对歌曲的打分 = 用户听此歌曲的次数 / 用户听单首歌曲的最大次数 这样就可以把打分数据规整到0~1之间,还是上面的数据: 3389    9527    23/56 3389    9528    56/56 3306    1211    78/78 3306    9527    45/78 用户听的最多的歌曲打分是1,其他歌曲的打分等于听歌次数除以最大次数,我们就获得了用户的打分数据了。剩下的工作就是按照标准的Slope One流程走了,程序代码可以参考:http://code.google.com/p/openslopeone/ 贴出几个实例大家看看,第一次做的结果,再去做的话应该比这个要好一些: 歌曲 推荐歌曲 南无大悲观世音菩萨   刘小茜 梵音大悲咒   齐豫 大悲咒   齐豫 观世音菩萨发愿偈.大悲咒   齐豫 大悲咒   邝美云 般若波罗蜜多心经   齐豫 大悲咒   齐豫 清净法身佛   齐豫 阿弥陀佛在心间   小娟 吉祥如意   凤凰传奇 好一朵茉莉花   朱昌耀 理查德-克莱德曼《梦中的婚礼》   合辑(欧美) 茉莉花(汉族民歌)   雷佳 好一朵茉莉花-笛子   合辑(内地) 最浪漫的事   赵咏华 沧海一声笑   罗文 how can i keep from singing   Enya 生死不离   ...