PHP构建高性能系统
如何解决系统中可能存在的性能问题呢?
首先,我们需要清楚在业务上有什么要样的性能需求;
第二步,根据性能的要求去考虑系统的设计,
第三步,系统的开发过程中去关注可能存在的局部性能问题。
评估系统的性能要求:
没有开发过性能敏感系统的团队,容易犯的错误是,不去考虑系统将来有多少人使用,并发访问有多高,需要存贮多少数量的数据? 直接就开始做系统的开发,抱着等着出了性能问题再说。系统做出来上线之后,性能的问题就暴露出来。但这时候,要解决好性能问题将要负出沉重的代价,往往是 一个系统的重写。 进度的压力成为了不考质量与性能的理由。造成一个现象:我们没有时间去把一个系统做好,但我们有时间去把同一个系统做上几遍。短期是高效率的,但长期是低效率的。
在开发一个新系统时,请搞清楚以下的几个问题:
- 系统的用户有多少? 万级 、 十万级、百万级、还是更多?
- 系统的使用的高峰阶段有多少人来使用? 每钞能处理多少个请求(QPS)?
- 系统存贮的数据记录有多少条? 数据量是G,10G,还是100G?
我举例说明一下:
*广告投放系统:
10个用户以对系统有写的访问, 1000万级的用户对系统有访问。
广告主要投放在网站上, 一个页面往往具有几个广告的展现。每天要达到上亿的PV。高峰的QPS所达到上十亿。
广告每天的数据量在1G以内,没有历史数据需要处理。
*互动贴吧:
千万级的用户量,并且都进行读写操作。
系统在热门期间,需要支持1亿的PV,高峰QPS 要达到1250 * 2 = 2500 QPS
贴吧的贴子数: 10000贴吧 * 1000主题 * 100 贴子 = 10亿个贴子; 一个贴子平均200个汉字,即有 400G的数据量。
*B2B网站:
用户量应具有百万级.
以每用户每天平均访问3次计算,那 100万 * 2 * 20(平均每次访问PV数)= 4000万的PV,在高峰期以平均的QPS*2 = 500 *2 = 1000 (QPS)
看到这些数据,大家都会有很大的疑问:
“太超前了,现在的业务发展情况远远不需要的这样高的性能业支持,如果以很大的代价来实现,实在是太浪费,并且业务当前所需要的紧急的问题,是功能、是易用性并不是性能。”
这的确是一个现实的情况,这里我将在第三部份来计论这个情况,现在假设我们需要应对这些高性能的挑战吧。
性能设计的指导原则
我们面对上千万级的用户、几千的QPS、百G以上的数据。 实在没有办法以单独的服务器来支撑,因此在设计系统时,第一个要考虑的就是:
系统的水平扩展能力:
大家应很容易就理解和认同这种做法。 但是做起来确并不是简单的事情,需要根据具体的应用来设计。
假设我们需要5000的QPS,那我分解成 500 * 10,使用10组服务器来支持。 这时我们需要考虑的问题是:
- 用户能使用不同的服务器来完成他的操作么?
- 用户的会话状态,是存贮在客户端,还是服务端?
- 服务端有数据同步么?怎么在多台服务器来实现
- 需要多少数据库服务器来存贮数据? 需要数据分割么?
在数据存贮方面,超过千万级的数据记录,上10G的数据,就考虑到水平扩展的能力。不论是我们常用的mysql还是oracle, 单表处理数据量是有一个性能拐点的。就需要考虑到折表,单机的数据库处理能力也是有限的,就要考虑到使用数据量的集群。
系统的水平扩展能力是解决性能敏感系统的首要原则,它使得系统具有通过增加更多的硬件设备来提高性能。
并且需要让这个扩展是容易进行的。
另外,即使我们系统有很好的水平提展能力,提搞单机的QPS还是非常必要。 单机的QPS太低,使得服务器的成本变得不可接受。
区别对待系统的读写访问
当面对高性能的访问时,你分析发现,在互联网的WEB应用,用户的读写访问在一般都会达到100:1,甚至会更高。高并发访问的性能问题,就会减化为, 如何提高并发的读访问速度和保证写访问的正确性有及时性。
在开发系统中,我们常常会出现两种以下的做法:
- 整个系统的开发追求性能,系统没有什么结构,所有的功能的实现都是,直接在页面拼写SQL来读写数据。
- 全面考虑程序的结构,追求系统的扩展性和可维性。
这两种方法,都有他们适用的范围,但同时也有他们的不足。
- 先说第二种方法,面对业务层面和系统层面的复杂性,抽象成为解决问题的有效手段。在抽象的指导下,封装、隐藏实现、隔离变化、分层都会出现系统的 设计中,但这样的设计带来的问题是间接层过多,在PHP这种动态脚本语言下,更多的代码、更多的调用层次都是会降低性能。 它适用于逻辑的复杂的系统,但性能要求在50(QPS)以下.
- 第一种方法,有一句来形容”在山顶上速度最快的下山方法就是从山顶上直接跳下来” 用直接数据修改的方法,无法有效表达抽象,无法管理各种变化的因素,数据将不在统一的逻辑约束下变更,我们将对变化失去控制,我们面对将是一座千疮百孔的 烂尾楼。 正确性虽着业务多次的修改,越来越得不到保证,这时性能也没有什么意义? 这种方式合适于逻辑简单的功能。
面对不同的要求,我们需要区别对待, 读写分离的思路,不仅是程序和程序开发模式的分离,它还意味着:
- 系统的体系结构的影响,建立集中写(单点写)、多点读的模式。 它包括前端服务器的部署、数据存贮和同步方案等。
- 可以使得读的逻辑尽可能的简单,不受写逻辑的干扰。有价值的放弃对于读访问逻辑的抽象,数据不会变更,不需要统一逻辑的约束。 面对性能的要求,我们可以弃程序的结构,以至于可重复某些代码。
- 面对简单的读逻辑我们可以有更多的性能优化方案。
- 读写分离也是解决军阀割据般的网络环境(电信、网通)的方法。
举例说明:
*联盟的广告展现代码不到100行。广告的展现代码不到500行(因为定向投放),其中的逻辑只是读取本地数据,套展现模版。 所有数据都邮后端推送到前端的WEB服务器。而广告数据的生成修改,就有很复杂的系统来管理,这基本不考虑性能的问题.
*互动组开发评论系统和贴吧系统,设定的考虑也是读写分离的模式,写逻辑强调代码的结构良好,读逻辑追求性能。
考虑使用缓冲数据的机制:
在越接用户和越接用具体应用时,缓冲的效率越高。 因此可能考虑在WEB服务器来建立一缓存。 在缓存机制的设计与使用,互动团队的贴吧系统应是值得学习的项目。当然我们也清楚那些好东西是做缓存的必备良药。Memched,BDB, varnish 需要清楚他们适合解决的问题是什么,加以利用。
不管Cache的机制如何有效,但我们还得保证在无Cache的情况下,性能基本满足业务的需要。因为Cache总是有他的命中率的,并且也要考虑到在Cache失效后,我们系统还是能正常运行的。
在我的理解,”’Cache是性能的放大器。”’
在系统开发过程中,怎么考虑处理性能的风险
在系统的开发过程的管理,本质上还是以风险驱动的,什么最可能产生严重的问题,就应该去优先去解决。在评估性能后,我们需要的是一个高性能要求的系统,并且系统一上线,就会马上遇到这种性能的压力,那么我建议如下:
- 跟据上述的设计原则来指导你设计,并与有经验的人进行计论
- 借着桩的方式,快速搭建系统框架,进行性能测试,确定整体的性能,整体的性能高于局部的性能。
- 解决是性能关键的子系统或组件的问题,这是解决局部性能问题。
- 在提交功能测试前进行性能测试,不要等上线之后才发现性能的问题。
对于进度紧张,并且预期在未来半年内不会遇到性能挑战,关于上述的第3点可以先不做。但你对于系统的当前状况一定要清楚。 在系统在性能挑战出现之后,就可从容的去解决性能的问题。