折腾了几个月的系统上线了,稳定的跑了一段时间之后,随着周边系统的陆续接入,日访问量达到了400万.
这个系统的特点是用户数和并发数比较稳定,并非有几百万离散的用户,而是固定的几百台客户端不停的刷.查询量高,写入量占1/10左右.通过日志分析,其中简单查询(不查询db)占1/3,可被db 稳定cache的查询占1/4,其余1/3强是针对大表的查询和改写操作.几乎没有静态页面.
基本构架是4台单cpu 服务器,linux操作系统,apache2+php5+mysql,2台前置,两台db,一台针对查询作了大量优化,除了标准http接口web服务,还支持xml/soap.通过监控脚本发现,cpu最高占用率不超过30%,接口最大响应时间低于900ms.总的来说,性能表现是比较理想的,还有相当的余地.
前期除了整体架构的考虑之外(并不很复杂),在性能方面作了些准备.
http/xml/soap作为接口协议本身在性能上是有一定的局限的,基于文本,一问一答,非面向长连接(虽然可以keepalive)的协议本身就比较"重",过多tag的存在会影响协议有效信息负载(通过一些针对性的优化设计,尽量减少xml的tag可以在一定程度上提高有效负载,但总是比紧凑的二进制协议要差).但做这个系统一个重要目标是提高其接口开放性,协议的可跟踪可维护,可快速扩充升级等特性,考虑到文本协议的可读性好,http/xml/soap在众多开发工具中已经得到广泛支持,开发资源较多,服务器和客户端构建速度快,且一个系统的性能瓶颈未必集中在协议上,经过权衡,还是采用了这种形式的接口协议.前几年用过C/C++/java甚至asp做过类似的接口,作起来也是轻车熟路.
选择php到不是我一开始的目标,使用了一段时间之后,觉得php确实很棒,开发效率高,文档和支持好,调试debug容易(print,var_dump,error_reporting),运行效率并不差(经过优化后).作为脚本语言,可运行程序本身就是代码,不用考虑什么内存泄露二进制不兼容等问题,修改升级可以跳过编译发布等刚性的环节,维护和加新功能简直就如闲庭信步.php真的是用来开发业务系统/网站服务的快刀(java很好但有点重,aspx离开ms就几乎不能活且慢如牛,曾经喜欢的perl貌似昨日黄花,python...有php了,暂时先不学了吧,学起来也很快),但是对于大量(几千万以上)数据分析计算密集计算,就比c语言差了不止一个数量级了--所以,C/C++是不能丢的.^_^
PHP语言有两个地方的性能比较容易成为瓶颈.第一是面向对象的特性,尤其是类继承之后,性能比直接函数调用下降一些,但由于这部分消耗从绝对值上讲并不大,考虑到面向对象可换来结构上的一些优点,所以是值得的.但除了几个核心可重用组件之外,大部分接口还是封装成函数调用提供出来的.
最头疼的是php这种脚本语言的加载->预编译->运行->销毁的特性,测试中发现,只要进行了”include_once/require”等包含性的调用,性能就呈几何级数的下降.我测试的案例是(通过ab(apache bench) 工具测试):
1) 在一个php里直接调用mysql查询api,单机性能是800请求/秒,
2) 将代码拆成两个文件进行了一下include_once,即下降到200多一点点.再include_once一些公共函数,即使不调用,则下降到100左右.
这个问题的解决方案是
1:尽量减少include,将大文件按功能和意图拆分成小文件,合理include
2:最见成效的优化: 安装APC(PHP: Alternative PHP Cache )这个开源组件可以给PHP带来300%的性能提升,简直是让人豁然开朗.
Zend有个优化工具,但安装之后不管怎么配置(也许是没倒点子上)都不见成效,反而跟apc有冲突,干脆禁用了
此外,安装了著名的memcache,将不常改变又被频繁查询的信息保存到memcache 里,效果确实如闪电一般的快.
memcache是不错的开源组件,除了速度之外,还有很大的优点运行稳定,接口简单.
关于memcache接口简单这个特点,也有两说,简单的接口可以让使用者focus在对数据的存取上,不须关心其他细节,缺点包括
1: 开发者没有明确的接口去遍历和检查cache中的信息(至少pecl的php 的memcache模块和libmemcache C client没有)
2:memcache 不负责保证内存中的数据和真实数据(db/磁盘)同步,也就是说cache数据有效性和准确性存在一定风险,.如使用者修改了信息,需要负责加载.
3:memcache用进程动态分配的内存而不是共享内存缓存数据,在极端情况下(进程崩溃)下数据会全部丢失.但memcache可是著名的稳定....
4:memcache通过tcp提供访问接口,一方面实现了可分布性,一方面tcp上也有开销(当没有达到某个数量级时可以忽略).memcache 网络层使用的是一个libevent库,这个库风格比较优雅,轻量,稳定性和效率不错(memcache就是最好的例子),值得学习研究一下.
总上所述,应用memcache大体上应遵循这个原则: 只关注其速度,不要求其安全性和准确性,通过应用逻辑自行保障.那么,对cache数据的过期时间,访问流程都要有合理的设计.一般来说,memcache可以很好的充当中等应用里数据加速的角色,而远远不是内存数据库什么的.
我的做法是一般的数据(可能会被不定期改变)保持在cache里不超过5分钟(5分钟被reload一次,对性能没有任何负面影响).访问流程是:读cache,成功则返回,查不到再查db/文件,并将结果写到cache.更新数据的时候同时刷新cache.
另外,通过阅读memcache的代码和帮助文档,找出可遍历memcache内容的方法,用php写了个小管理工具,一旦发现数据可能有问题,通过此工具查到key和数据,将其修改或删除.原理是通过tcp连接向memcache发送stats items和stats cachedump命令,解析返回包(都是字符串),就这么简单.
这段时间还使用了几个不错的开源工具:
awstats:非常不错的网站流量统计,每天都通过这个看流量
jpgraph:php图表工具,不比java的jfreechart弱.见PHP图表库jpgraph:安装和中文支持
cronlog,可以轻松的将apache访问日志按天分割.
snoopy:一个轻巧的php http client,可以分析页面连接,简直可以当网络爬虫的基础组件了.
tbs:将php和html页面彻底分离的一个好组件,最大的特点是Tiny But Strong,不用引入几百个文件的什么framwork,只有几个php,几个接口调用,一些简单规则,一个页面的manul, 轻巧好用。当然,这玩艺也不是万能的,对于复杂的html格式,字符串拼装还是王道。
系统的稳定运行以及功能的准确完整还需要大量的监控和优化,尤其是针对web应用的响应速度和用户体验持续优化,需要引入诸如生成静态页面和javascript脚本,ajax,shtml等应用技术,以及数据抽取整理(ETL),数据分析,特定场景下的搜索(xml搜索,全文检索,lucene搜索等).