在高负载网站中使用PHP的策略

在你回答这个问题之前,我从来没有开发过任何stream行的东西来达到高服务器负荷 把我当成(叹气)刚刚降落在地球上的外星人,虽然知道PHP和一些优化技术。


我正在开发一个PHP工具,可以达到相当多的用户,如果工作正确的话。 然而,尽pipe我完全有能力开发这个程序,但在处理巨大stream量的时候,我却一无所知。 所以这里有几个问题(可以随意将这个问题转化为资源线程)。

数据库

目前我打算使用PHP5中的MySQLifunction。 但是,我应该如何设置与用户和内容有关的数据库? 我真的需要多个数据库吗? 目前所有的东西都混杂在一个数据库中 – 尽pipe我一直在考虑把用户数据分散到另一个数据库,实际内容到另一个数据库,最后是核心站点内容(模板主人等)到另一个数据库。 我的理由是,发送查询到不同的数据库将缓解作为一个数据库= 3加载源的负载。 如果他们都在同一台服务器上,这还会有效吗?

高速caching

我有一个模板系统,用于构build页面和换出variables。 主模板存储在数据库中,每次调用一个模板时,都会调用它的caching副本(一个html文档)。 目前,我在这些模板中有两种types的variables – 静态variables和dynamicvariables。 静态variables通常是页面名称,网站名称 – 不经常变化的东西; dynamicvariables是每个页面负载变化的东西。

我的问题是:

说我对不同的文章有评论。 这是一个更好的解决scheme:每次加载页面时存储简单的评论模板并呈现评论(从数据库调用),或者将评论页面的caching副本存储为html页面 – 每次添加/编辑评论该页面被recached。

最后

有没有人有任何提示/指针在PHP上运行一个高负载的网站。 我很确定这是一个可行的语言使用 – Facebook和雅虎! 给它很大的优先权 – 但是有没有我应该注意的经验?

没有两个网站是相同的。 你真的需要得到一个工具,如Jmeter和基准,看看你的问题点将是。 你可以花费大量的时间进行猜测和改进,但是直到你测量和比较你的改变,你才会看到真正的结果。

例如,多年来,MySQL查询caching是我们所有性能问题的解决scheme。 如果你的网站很慢,MySQL专家build议打开查询caching。 事实certificate,如果你有一个很高的写入负载,caching实际上是瘫痪的。 如果你没有testing就打开它,你永远不会知道。

不要忘记,你永远不会缩放。 处理10req / s的站点将需要更改以支持1000req / s。 如果你足够支持10,000req / s,你的架构可能看起来完全不同。

数据库

  • 不要使用MySQLi – PDO是'现代'OO数据库访问层。 在查询中使用最重要的function是占位符。 它足够聪明,可以为你使用服务器端准备和其他优化。
  • 在这一点上你可能不想破坏你的数据库。 如果你发现一个数据库没有被削减,有几种技术可以扩展,这取决于你的应用程序。 如果读取次数多于写入次数,则复制到其他服务器通常效果不错。 分片是一种将数据分割到多台机器上的技术。

高速caching

  • 你可能不想在你的数据库中caching。 数据库通常是你的瓶颈,所以增加更多的IO通常是一件坏事。 有几个PHPcaching,完成类似的东西,如APC和Zend。
  • 用caching打开和closures来测量您的系统。 我敢打赌,你的caching比直接提供页面更重。
  • 如果需要很长时间才能从数据库构build您的评论和文章数据,请将memcache集成到您的系统中。 您可以caching查询结果并将它们存储在memcached实例中。 请记住,从memcache中检索数据必须比从数据库中组装数据库更快,以查看任何好处。
  • 如果您的文章不是dynamic的,或者在生成后有简单的dynamic更改,请考虑将html或php写入磁盘。 你可以在这个文章的磁盘上find一个index.php页面,如果有的话,它会把它stream到客户端。 如果不是,则生成文章,将其写入磁盘并发送给客户端。 从磁盘删除文件将导致页面被重写。 如果评论被添加到文章,删除caching的副本 – 它将被重新生成。

我是拥有超过1500万用户的网站的首席开发人员。 我们已经有很小的缩放问题,因为我们已经预先计划好了,并且仔细缩放了。 以下是我可以从我的经验中build议的一些策略。

SCHEMA首先,使你的模式非规范化。 这意味着,而不是有多个关系表,你应该select有一个大表。 一般来说,连接是浪费宝贵的数据库资源,因为执行多个准备工作和整理会烧毁磁盘I / O。 避免他们,当你可以。

这里的权衡是你将存储/提取冗余数据,但是这是可以接受的,因为数据和笼内带宽非常便宜(更大的磁盘),而多个准备I / O的成本要高得多(更多的服务器) 。

INDEXING确保您的查询至less使用一个索引。 但是要小心,如果你经常写或更新,索引会花费你。 有一些实验技巧可以避免这种情况。

您可以尝试添加未编制索引的其他列,这些列与索引的列并行运行。 然后,您可以有一个脱机进程,将非索引列分批写入索引列。 这样,当mySQL需要重新计算索引时,你可以更好地控制。

避免像瘟疫计算查询。 如果您必须计算查询,请在写入时尝试执行一次。

caching我强烈build议Memcached。 它已经被PHP堆栈(Facebook)上的最大玩家所证实,并且非常灵活。 有两种方法可以做到这一点,一个是caching在你的数据库层,另一个caching在你的业务逻辑层。

数据库层选项需要caching从数据库检索到的查询结果。 您可以使用md5()来散列您的SQL查询,并在将数据库用作查找键之前使用它。 这个好处是它很容易实现。 不利的一面(取决于实现)是因为你将所有caching视为caching过期而失去了灵活性。

在我工作的商店中,我们使用业务层caching,这意味着我们系统中的每个具体类都控制着自己的caching模式和caching超时。 这对我们来说工作得非常好,但是请注意,从数据库检索到的项目可能与caching项目不同,因此您必须一起更新caching和数据库。

数据复制只有复制到目前为止。 比你想象的要迟,你的写作将成为瓶颈。 为了弥补,确保尽可能早地支持数据分片。 如果你不这样做,你可能会想要拍摄自己。

实现起来相当简单。 基本上,您想要将密钥权限与数据存储分开。 使用全局数据库来存储主键和簇ID之间的映射。 您查询此映射以获取群集,然后查询群集以获取数据。 你可以caching这个查询操作,这将使其成为一个微不足道的操作。

这样做的缺点是可能难以拼凑来自多个碎片的数据。 但是,你也可以devise你的方式。

离线处理如果用户不需要,请不要让用户等待您的后端。 构build一个作业队列,并移动任何可以脱机的处理,与用户的请求分开。

我曾在几个网站上获得数百万/点/月的PHP和MySQL支持。 这里有一些基础知识:

  1. caching,caching,caching。 caching是减lessWeb服务器和数据库负载的最简单和最有效的方法之一。 caching页面内容,查询,昂贵的计算,任何I / O绑定。 Memcache简单而有效。
  2. 一旦完成,请使用多个服务器。 您可以有多个Web服务器和多个数据库服务器(带有复制)。
  3. 减less对您的networking服务器的总体要求。 这需要使用expires头来cachingJS,CSS和图像。 您也可以将您的静态内容移动到CDN,这将加快您的用户体验。
  4. 测量和基准。 在您的生产机器上运行Nagios并在您的dev / qa服务器上加载testing。 你需要知道什么时候你的服务器会着火,所以你可以防止它。

我build议阅读build立可扩展的网站 ,它是由Flickr的工程师之一写的,是一个很好的参考。

查看关于可伸缩性的博客文章,它有很多关于使用多种语言和平台进行扩展的演示链接: http : //www.ryandoherty.net/2008/07/13/unicorns-and-scalability/

Re:PDO / MySQLi / MySQLND

@ 加里

你不能只是说“不要使用MySQLi”,因为他们有不同的目标。 PDO几乎就像一个抽象层(尽pipe实际上并不是这样),并且devise成使得使用多个数据库产品变得容易,而MySQLi则专用于MySQL连接。 在与MySQLi进行比较的背景下,PDO是现代访问层是错误的,因为您的语句暗示进程是mysql – > mysqli – > PDO,而不是这种情况。

MySQLi和PDO之间的select很简单 – 如果您需要支持多个数据库产品,那么您使用PDO。 如果你只是使用MySQL,那么你可以selectPDO和MySQLi。

那么为什么要select通过PDO的MySQLi呢? 见下文…

@ross

您对MySQLnd是最新的MySQL核心语言级库是正确的,但它不是MySQLi的替代品。 MySQLi(与PDO一样)仍然是您通过PHP代码与MySQL进行交互的方式。 这两个都使用libmysql作为PHP代码背后的C客户端。 问题在于libmysql不在核心PHP引擎之内,而mysqlnd也是在这个地方使用的,也就是说它是一个本地驱动程序,它利用核心PHP内部实现效率最大化,特别是在涉及内存使用情况的地方。

MySQLnd正在由MySQL自己开发,最近登陆到RCtesting的PHP 5.3分支上,准备在今年晚些时候发布。 然后,您将能够使用Mysql与MySQLi …但不能与PDO。 这将使MySQLi在许多领域(不是全部) 获得性能提升 ,如果不需要像PDO的function那样的抽象,它将成为MySQL交互的最佳select。

也就是说,MySQLnd 现在可以在PHP 5.3中用于PDO,所以你可以从ND到PDO中获得性能提升的优势,然而,PDO仍然是一个通用的数据库层,所以不太可能从中受益ND的增强与MySQLi一样 。

一些有用的基准可以在这里find,虽然他们是从2006年。你还需要知道像这样的选项 。

在决定MySQLi和PDO时,需要考虑很多因素。 在现实中,直到你得到高要求的数字才是重要的,在这种情况下,使用专门为MySQLdevise的扩展更有意义,而不是一个抽象的东西,恰好提供了一个MySQL驱动。

哪一个最好,因为各有利弊。 你需要阅读我提供的链接,并提出自己的决定,然后testing并找出答案。 我在过去的项目中使用了PDO,这是一个很好的扩展,但是我select纯粹的性能将MySQLi与新的MySQLND选项编译(当PHP 5.3发布时)。

一般

  • 在开始看到现实世界的负载之前,不要尝试优化。 你可能会猜对,但是如果你不这样做,你就浪费了时间。
  • 使用jmeter , xdebug或其他工具来对网站进行基准testing。
  • 如果加载开始成为一个问题,可能会涉及对象或数据caching,所以通常需要读取caching选项(memcached,MySQLcaching选项)

  • 分析代码,以便知道瓶颈在哪里,以及代码或数据库中

数据库

  • 如果移植到其他数据库不重要 ,使用MYSQLi ,否则使用PDO
  • 如果基准testing显示数据库是问题,请在开始caching之前检查查询。 使用EXPLAIN查看你的查询在哪里放缓。
  • 在优化查询并以某种方式caching数据库之后,可能需要使用多个数据库。 复制到多个服务器或分片(在多个数据库/服务器上分割数据)可能是合适的,具体取决于数据,查询以及读/写行为的types。

高速caching

  • 已经完成了对caching代码,对象和数据的大量写作。 查阅关于APC , Zend Optimizer , memcached , QuickCache , JPCache的文章 。 在你真的需要之前做一些这样的事情,而不会担心开始未优化。
  • APC和Zend Optimizer是操作码caching,它们通过避免重新编码和重新编译代码来加速PHP代码。 一般安装简单,值得早做。
  • Memcached是一个通用caching,可以用来caching查询,PHP函数或对象,或整个页面。 代码必须专门编写才能使用,如果没有中心点来处理创build,更新和删除caching对象,代码可能是一个涉及的过程。
  • QuickCache和JPCache是​​文件caching,否则类似于Memcached。 基本的概念是简单的,但也需要代码,更容易创build,更新和删除的中心点。

  • 考虑高负载的替代networking服务器。 像lighthttp和nginx这样的服务器可以在比Apache更less的内存中处理大量的stream量,如果你可以牺牲Apache的能力和灵活性(或者你不需要那些经常不需要的东西的话)。
  • 请记住,现在硬件的价格非常便宜,所以一定要花费大量精力来优化大量代码,而不是“让我们买一个怪兽服务器”。
  • 考虑在这个问题中添加“MySQL”和“scaling”标签

APC是绝对必须的。 它不仅是一个伟大的caching系统,但从自动caching的PHP文件的收益是一个天赐之物。 至于多个数据库的想法,我不认为你会在同一台服务器上有不同的数据库。 在查询期间它可能会让你的速度有所提升,但是我怀疑部署和维护这三个代码所花费的时间,同时确保它们同步也是值得的。

我也强烈build议运行Xdebug来查找程序中的瓶颈。 它使我对优化变得轻而易举。

首先,我认为克努特说:“过早优化是万恶之源”。 如果您现在不需要处理这些问题,那么请不要专注于提供可以正确运行的东西。 这就是说,如果优化不能等待。

尝试分析你的数据库查询,找出什么是缓慢的,发生了什么,并从中提出一个优化策略。

我会调查Memcached,因为这是很多较高负载的网站用来有效地caching所有types的内容,PHP对象接口是相当不错的。

在服务器之间拆分数据库并使用某种负载平衡技术(例如,在1到#个冗余数据库之间用必要数据生成一个随机数,并使用这个数来确定连接到哪个数据库服务器)也是一个很好的方法来增加效率。

过去对于一些相当高负载的站点,这些都已经相当成功。 希望这有助于让你开始:-)

用Xdebug(比如推荐的tj9991)来分析你的应用肯定是必须的。 盲目地去优化事情并没有太多的意义。 Xdebug将帮助你在代码中find真正的瓶颈,这样你可以明智地花费你的优化时间,并修复实际导致速度下降的代码块。

如果您使用Apache,另一个可以帮助testing的工具是Siege 。 它将帮助您预测服务器和应用程序如何通过真正的步伐来应对高负载。

任何types的PHP操作码caching(如APC或许多其他的)也会有很大的帮助。

我每月运行一个网页浏览量为7-8百万的页面。 不是非常多,但足以让我们的服务器感到负载。 我们select的解决scheme很简单:在数据库级别的Memcache。 如果数据库负载是您的主要问题,此解决scheme运作良好。

我们开始使用Memcache来caching整个对象以及最常用的数据库结果。 它确实有效,但它也引入了错误(如果我们更加小心的话,可能会避免其中的一些)。

所以我们改变了方法。 我们构build了一个数据库包装器(使用与旧数据库完全相同的方法,因此很容易切换),然后我们将其分类以提供memcached数据库访问方法。

现在,您只需确定查询是否可以使用caching(可能过期)的结果。 现在大部分用户运行的查询都直接从Memcache中获取。 例外情况是更新和插入,对于主要网站来说只是由于日志logging而发生的。 这个相当简单的措施使我们的服务器负载减less了大约80%。

感谢关于PHPcaching扩展的build议 – 你能解释一个使用另一个的理由吗? 我通过IRC了解了memcached的大部分内容,但从来没有听说过APC,您对此有何看法? 我假设使用多个caching系统是相当有效的。

实际上, 许多人一起使用APC和memcached 。

看起来我错了 。 MySQLi仍在开发中。 但根据文章,PDO_MySQL现在正由MySQL团队贡献。 从文章:

MySQL改进的扩展 – mysqli – 是旗舰。 它支持MySQL服务器的所有function,包括字符集,预处理语句和存储过程。 驱动程序提供了混合API:您可以根据自己的喜好使用程序或面向对象的编程风格。 mysqli自带PHP 5以上。 请注意,PHP 4的生命周期结束于2008-08-08。

PHP数据对象(PDO)是一个数据库访问抽象层。 PDO允许您为各种数据库使用相同的API调用。 PDO不提供任何程度的SQL抽象。 PDO_MYSQL是PDO的MySQL驱动程序。 PDO_MYSQL附带PHP 5.从PHP 5.3开始,MySQL开发人员积极贡献它。 一个统一的API的PDO好处的价格是MySQL特定的function,例如多个语句,并没有通过统一的API完全支持。

请停止使用已发布的PHP的第一个MySQL驱动程序:ext / mysql。 自2004年推出MySQL改进型扩展(mysqli)以来,PHP5没有理由继续使用最老的驱动程序。 ext / mysql不支持Charsets,Prepared Statements和Stored Procedures。 它仅限于MySQL 4.0的function集。 请注意,MySQL 4.0的扩展支持截止于2008-12-31。 不要把自己限制在这样的旧软件的function集中! 升级到mysqli,另请参阅Converting_to_MySQLi。 从我们的angular度来看,mysql只处于维护模式。

对我来说,这篇文章似乎偏向于MySQLi。 我想我偏向于PDO。 我真的很喜欢在MySQLi上使用PDO。 这对我来说很简单。 这个API与我编写的其他语言非常接近.OO数据库接口似乎更好。

我还没有遇到任何通过PDO不可用的特定MySQLfunction。 如果我做过,我会感到惊讶。

对于什么是值得的,即使没有像memcached这样的扩展/帮助程序包,caching在PHP中也是DIRT SIMPLE。

所有你需要做的就是使用ob_start()创build一个输出缓冲区。

创build全局cachingfunction。 调用ob_start ,将该函数作为callback函数传递。 在函数中,查找页面的caching版本。 如果存在,则服务并结束。

如果不存在,脚本将继续处理。 当它到达匹配的ob_end()时,它会调用你指定的函数。 那时候,你只是得到输出缓冲区的内容,把它们放在一个文件中,保存文件,然后结束。

添加一些过期/垃圾收集。

许多人不知道你可以嵌套ob_start() / ob_end()调用。 所以,如果你已经使用输出缓冲来parsing广告,或者做语法高亮或者其他任何事情,你可以嵌套另一个ob_start/ob_end调用。

PDO也很慢,其API非常复杂。 如果可移植性不是问题,那么没有人理智的使用它。 让我们面对它,在99%的所有networking应用程序不是。 你只要坚持使用MySQL或PostrgreSQL,或者你正在使用的任何东西。

至于PHP的问题和考虑到什么。 我认为不成熟的优化是万恶之源。 ;)首先完成你的应用程序,在编程时尽量保持干净,做一些文档编写unit testing。 有了以上所有的内容,你就不会在重新编码的时候出现问题。 但是,首先你要做的,并推出来看看人们如何反应。

当然pdo是不错的,但是关于性能与mysql和mysqli 有 一些争议,虽然现在看起来是固定的。

如果你设想可移植性,你应该使用pdo,但是如果不是,mysqli应该是这样的。 它有一个面向对象的接口,准备好的语句,以及pdo提供的大部分内容(除了可移植性)。

另外,如果真的需要性能的话,请准备PHP 5.3中的(native mysql) MysqLnd驱动程序, 这个驱动程序将与php更紧密地集成在一起,具有更好的性能和更好的内存使用率(以及性能调优的统计数据)。

如果你有群集服务器(和类似YouTube的负载),Memcache是​​很好的,但是我也会先尝试一下APC 。

已经给出了很多很好的答案,但我想指出一个名为XCache的替代操作码caching。 它由一个轻微贡献者创build。

而且,如果将来您可能需要对数据库服务器进行负载平衡, MySQL代理可以很好地帮助您实现这一点。

这两种工具都应该很容易地插入到现有的应用程序中,所以这种优化可以在需要的时候完成,而没有太多的麻烦。

第一个问题是你真的期望它有多大? 你计划投资于你的基础设施多less? 既然你觉得有必要在这里提出这个问题,我猜你希望在有限的预算下开始小。

如果网站不可用,性能是无关紧要的。 而为了可用性,你需要水平缩放。 你可以明智地逃脱的最低限度是2服务器,无论是运行Apache,PHP和MySQL。 设置一个DBMS作为另一个的奴隶。 在主服务器上执行所有的写操作,以及在本地数据库上的所有读操作(不pipe是什么) – 除非由于某种原因,你需要读回刚才读取的数据(使用主服务器)。 确保你已经有了机械来自动提升奴隶并围住主人。 对Web服务器地址使用循环法DNS来为从节点提供更多的亲和力。

在这个阶段将数据分区到不同的数据库节点是一个非常糟糕的主意 – 不过,您可能需要考虑将其拆分到同一台服务器上的不同数据库(这将有助于在超过Facebook时跨节点进行分区)。

确保你有监测和数据分析工具来衡量你的网站的性能和发现瓶颈。 大多数性能问题可以通过编写更好的SQL /修复数据库模式来解决。

保持模板caching在数据库上是一个愚蠢的想法 – 数据库应该是结构化数据的中央公共存储库。 保持你的模板caching在你的web服务器的本地文件系统上 – 它将会更快,并且不会减慢你的数据库访问速度。

请使用操作码caching。

花大量的时间来研究你的网站和日志,以了解为什么它会这么慢。

尽可能多的caching到客户端。

使用mod_gzip压缩你所能做的一切。

C。

我的第一条build议是在devise网站时考虑这个问题,并牢记在心,但不要过分 。 预测一个新网站的成功往往是很困难的,而且我会花更多的时间在早期完成并在以后进行优化。

一般来说, 简单是快速的 。 模板减慢你的速度。 数据库减慢你的速度。 复杂的图书馆让你失望。 彼此分层模板,从数据库中检索它们,并在复杂的库中parsing它们 – >时间延迟相互之间相乘。

一旦你有基本的网站启动并运行testing ,告诉你在哪里花费你的努力。 很难看到目标。 通常要加快速度,你将不得不解开代码的复杂性,这使得维护变得越来越难,所以你只想在必要的时候去做。

以我的经验build立数据库连接是相对昂贵的。 如果您可以避开它,请不要连接到数据库,以便访问stream量最大的页面上的常规访问者,例如站点的首页。 创build多个数据库连接是非常有益的疯狂。

加里

不要使用MySQLi – PDO是'现代'OO数据库访问层。 在查询中使用最重要的function是占位符。 它足够聪明,可以为你使用服务器端准备和其他优化。

我现在正在嘲笑PDO,看起来你是对的 – 但是我知道MySQL正在开发PHP的MySQLd扩展 – 我认为要么成功的MySQL或MySQLi – 你怎么看?


@ Ryan , Eric , tj9991

感谢关于PHPcaching扩展的build议 – 你能解释一个使用另一个的理由吗? 我通过IRC了解了memcached的大部分内容,但从来没有听说过APC,您对此有何看法? 我假设使用多个caching系统是相当有效的。

我一定会整理一些分析testing人员 – 非常感谢你对这些build议。

我没有看到自己很快从MySQL切换 – 所以我想我不需要PDO的抽象能力。 感谢那些DavidM的文章,他们帮了我很多。

查看Apache Web服务器的输出cachingmod_cache ,与ASP.NET中的输出caching类似 。

是的,我可以看到它仍然是实验性的,但总有一天会是最终的。

我不能相信没有人已经提到过:模块化和抽象化。 如果你认为你的网站将不得不成长到很多机器,你必须devise它,所以它可以! 这意味着愚蠢的东西,不要假设数据库是在本地主机上。 这也意味着一开始就会有些麻烦,比如写一个数据库抽象层(比如PDO,但要轻得多,因为它只做你需要的)。

这意味着像使用框架一样的工作。 您需要在代码中使用图层,以便您稍后可以通过重构数据抽象层来获得性能,例如通过教授某些对象位于不同的数据库中,而代码不必知道或关心

最后,要小心内存密集的操作,例如不必要的string复制。 如果您可以保持PHP的内存使用率不变,那么您的Web服务器将获得更高的性能,而当您使用负载平衡的解决scheme时,这将会扩展。

如果您正在处理大量数据,并且caching不能削减,请查看Sphinx。 我们使用SphinxSearch获得了很好的结果,不仅用于更好的文本search,而且还用作处理大型表格时MySQL的数据检索replace。 如果你使用SphinxSE(MySQL插件),它超过了我们从caching几次以来的性能提升,而应用程序实现是一个罪魁祸首。

关于caching的要点是点亮的; 这是构build高效应用程序中最不复杂和最重要的部分。 我想补充一点,虽然memcached很棒,但是如果您的应用程序存在于一台服务器上,APC的速度要快五倍。

MySQL性能博客上的“caching性能比较”文章对这个主题有一些有趣的基准 – http://www.mysqlperformanceblog.com/2006/08/09/cache-performance-comparison/