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

在你回答这个问题之前,我从来没有开发过任何流行到足以达到高服务器负载的东西。请把我当作(唉)一个刚刚登陆地球的外星人,尽管我知道PHP和一些优化技术。


我在PHP开发一个工具,可以获得相当多的用户,如果它工作正确。然而,虽然我完全有能力开发程序,但当涉及到制作可以处理巨大流量的东西时,我几乎一无所知。所以这里有一些关于它的问题(也可以把这个问题变成一个资源线程)。

数据库

目前我计划在PHP5中使用MySQLi特性。但是,我应该如何设置与用户和内容相关的数据库呢?我实际上需要多个数据库吗?目前,所有的东西都混杂在一个数据库中——尽管我一直在考虑将用户数据分散到一个数据库中,将实际内容分散到另一个数据库中,最后将核心站点内容(模板master等)分散到另一个数据库中。我这样做的理由是,将查询发送到不同的数据库将减轻它们的负载,因为一个数据库= 3个负载源。另外,如果它们都在同一个服务器上,这仍然有效吗?

缓存

我有一个用于构建页面和交换变量的模板系统。主模板存储在数据库中,每当一个模板被调用时,它的缓存副本(html文档)就会被调用。目前,我在这些模板中有两种类型的变量-静态变量和动态变量。静态变量通常是像页面名称,网站的名称-不经常改变的东西;动态变量是在每次页面加载时改变的东西。

我的问题是:

比如说我对不同的文章有评论。这是一个更好的解决方案:存储简单的注释模板,并在每次页面加载时呈现注释(来自DB调用),或者将注释页面的缓存副本存储为html页面——每次添加/编辑/删除注释时,页面都会被重新检索。

最后

有人有任何提示/指针运行一个高负载的PHP网站。我很确定这是一种可行的语言——Facebook和Yahoo!优先考虑——但有什么经验是我应该注意的吗?

26810 次浏览

APC是绝对必须的。它不仅是一个伟大的缓存系统,而且从自动缓存的PHP文件中获得的好处是天赐良机。至于多数据库的想法,我认为在同一台服务器上使用不同的数据库不会有什么好处。它可能会在查询时提高一些速度,但我怀疑为确保三者同步而部署和维护代码所付出的努力是否值得。

我还强烈建议运行Xdebug来查找程序中的瓶颈。它使优化对我来说轻而易举。

首先,正如Knuth所说,“过早的优化是万恶之源”。如果你现在不需要处理这些问题,那就不要去做,先专注于交付一些正确工作的东西。也就是说,如果优化不能等待。

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

我会研究Memcached,因为它是许多高负载站点用于有效缓存所有类型的内容的,并且它的PHP对象接口相当不错。

在服务器之间分割数据库并使用某种负载平衡技术(例如,在具有必要数据的冗余数据库中生成1到#之间的随机数—并使用该数字确定要连接到哪个数据库服务器)也是提高效率的一种极好的方法。

在过去,对于一些相当高的负载站点,这些方法都非常有效。希望这能帮助你开始:-)

我曾经在一些网站上工作过,这些网站都是由PHP支持的,每个月都能获得数百万/点击量;MySQL。以下是一些基本知识:

  1. 缓存,缓存,缓存。缓存是减少web服务器和数据库负载的最简单和最有效的方法之一。缓存页面内容,查询,昂贵的计算,任何受I/O限制的东西。Memcache非常简单有效。
  2. 使用多个服务器,一旦你达到极限。您可以拥有多个web服务器和多个数据库服务器(带有复制)。
  3. 减少对服务器的请求总数。这需要使用expires headers缓存JS, CSS和图像。您还可以将静态内容移动到CDN,这将加快用户的体验。
  4. 测量,基准。在生产机器上运行Nagios,在开发/qa服务器上进行负载测试。你需要知道你的服务器什么时候会着火,这样你才能预防它。

我推荐阅读构建可扩展的网站,它是由一个Flickr工程师写的,是一个很好的参考。

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

使用Xdebug(推荐使用tj9991)之类的工具对应用程序进行性能分析绝对是必须的。盲目地进行优化是没有意义的。Xdebug将帮助您找到代码中真正的瓶颈,这样您就可以明智地花费优化时间,并修复实际上导致速度变慢的代码块。

如果你正在使用Apache,另一个可以帮助测试的实用程序是围攻。它将帮助您预测服务器和应用程序对高负载的反应,从而真正实现它的运行速度。

任何类型的PHP操作码缓存(如APC或其他)也会有很大帮助。

没有两个站点是相同的。你真的需要像jmeter和benchmark这样的工具来查看你的问题点在哪里。您可以花费大量的时间来猜测和改进,但是在您度量和比较您的更改之前,您不会看到真正的结果。

例如,多年来,MySQL查询缓存是我们所有性能问题的解决方案。如果你的站点很慢,MySQL专家建议打开查询缓存。事实证明,如果你有一个高的写负载,缓存实际上是瘫痪的。如果你不经过测试就打开它,你永远不会知道。

别忘了,缩放永远不会结束。处理10req/s的站点将需要更改以支持1000req/s。如果您足够幸运,需要支持10,000req/s,那么您的体系结构可能也会完全不同。

数据库

  • 不要使用MySQLi——PDO是“现代”的OO数据库访问层。最重要的功能是在查询中使用占位符。使用服务器端准备和其他优化也足够聪明。
  • 此时您可能不希望破坏数据库。如果你确实发现一个数据库没有被切断,有几种技术可以扩展,这取决于你的应用程序。如果你读的比写的多,复制到其他服务器通常会很好。分片是一种在多台机器上分割数据的技术。

缓存

  • 您可能不想在数据库中缓存。数据库通常是您的瓶颈,因此向数据库中添加更多IO通常不是一件好事。有一些PHP缓存可以完成类似的事情,比如APC和Zend。
  • 使用缓存开关来测量系统。我敢打赌你的缓存比直接提供页面要重。
  • 如果从数据库中构建评论和文章数据需要很长时间,请将memcache集成到您的系统中。您可以缓存查询结果并将它们存储在memcached实例中。从memcache中检索数据必须比从数据库中组装数据要快,这一点很重要。
  • 如果您的文章不是动态的,或者在生成后进行了简单的动态更改,请考虑将html或php写入磁盘。你可以有一个index.php页面,它在磁盘上查找文章,如果有,它就把文章流到客户端。如果不是,则生成文章,将其写入磁盘并将其发送给客户端。从磁盘删除文件将导致重新写入页面。如果在文章中添加了评论,请删除缓存的副本——它将重新生成。

@加里

不要使用MySQLi——PDO是“现代的”OO数据库访问层。最重要的功能是在查询中使用占位符。使用服务器端准备和其他优化也足够聪明。

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


@__abc0, __abc1, __abc2

谢谢你关于PHP缓存扩展的建议——你能解释一下为什么要使用一个而不是另一个吗?我听说过通过IRC的memcached很棒,但从来没有听说过APC -你对它们有什么看法?我认为使用多个缓存系统会适得其反。

我肯定会挑选一些测试人员,非常感谢你的建议。

当然,pdo是很好的,但有 一些的争议,相对于mysql和mysqli的性能,虽然现在似乎是固定的。

如果您考虑可移植性,您应该使用pdo,如果不考虑可移植性,则应该使用mysqli。它有一个面向对象接口、准备好的语句和pdo提供的大部分功能(除了可移植性)。

另外,如果确实需要性能,可以准备PHP 5.3中的(原生mysql) MysqLnd驱动程序,它将与PHP更加紧密地集成,具有更好的性能和改进的内存使用(以及用于性能调优的统计数据)。

如果你有集群服务器(和youtube一样的负载),Memcache是很好的,但我也会先尝试APC

它看起来像我错了。MySQLi仍在开发中。但是根据这篇文章,PDO_MySQL现在由MySQL团队贡献。摘自文章:

MySQL改进扩展- mysqli -是旗舰。它支持MySQL服务器的所有功能,包括 字符集,准备报表和 存储过程。司机提供了一个 混合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开发人员积极地为它做出贡献。的PDO好处 统一API的代价是 例如,MySQL的特定特性 多重陈述,都是不完整的

请停止使用第一个MySQL PHP驱动程序: ext / mysql。自从引进 MySQL扩展名:mysqli 在2004年的PHP 5中,没有理由继续使用最老的驱动程序 周围。Ext /mysql不支持 字符集,准备报表和 存储过程。它仅限于 MySQL 4.0的特性集。请注意 扩展支持MySQL 4.0结束于2008-12-31。不要把自己限制在这样的特性集中 老的软件!升级到mysqli,参见 也Converting_to_MySQLi。Mysql在 从我们的角度来看,只有维护模式 的观点。< / p >

对我来说,这篇文章似乎偏向MySQLi。我想我偏向于PDO。 我真的很喜欢PDO胜过MySQLi。这对我来说很简单。这个API更接近于我编写的其他语言。OO数据库接口似乎工作得更好

我还没有遇到过任何PDO无法提供的MySQL特性。如果有的话,我才会惊讶呢。

回复:PDO / MySQLi / MySQLND

@加里

你不能说“不要使用MySQLi”,因为他们有不同的目标。PDO几乎就像一个抽象层(尽管实际上不是),它的设计目的是为了方便使用多个数据库产品,而MySQLi则专门针对MySQL连接。在将PDO与MySQLi进行比较的情况下,说PDO是现代访问层是错误的,因为你的声明暗示了进程已经是mysql -> MySQLi -> PDO,而事实并非如此。

MySQLi和PDO之间的选择很简单——如果你需要支持多个数据库产品,那么就用PDO。如果你只使用MySQL,那么你可以在PDO和MySQLi之间选择。

那么你为什么选择MySQLi而不是PDO呢?见下文……

25011年< a href = " https://stackoverflow.com/questions/24675/tactics-for-using-php-in-a-high-load-site " > @ross < / >

你说的MySQLnd是正确的,它是MySQL最新的核心语言级库,但是它并不能取代MySQLi。MySQLi(与PDO一样)仍然是通过PHP代码与MySQL交互的方式。它们都使用libmysql作为PHP代码背后的C客户端。问题是libmysql在PHP核心引擎之外,这就是mysqlnd的作用,即它是一个本地驱动程序,利用PHP核心内部来最大化效率,特别是在内存使用方面。

MySQLnd是由MySQL自己开发的,最近已经登陆到PHP 5.3分支,该分支正在RC测试中,准备在今年晚些时候发布。然后你将能够使用mysqnd与MySQLi…但PDO不行。这将使MySQLi 性能提升在许多领域(不是全部),并将使它成为MySQL交互的最佳选择,如果你不需要像PDO那样的抽象功能。

也就是说,MySQLnd 现在在PHP 5.3中可用用于PDO,因此您可以从ND到PDO获得性能增强的优势,然而,PDO仍然是一个通用的数据库层,因此将是不可能像MySQLi那样从ND的增强中获益

这里可以找到一些有用的基准测试虽然他们是2006年。你还需要注意像这个选项这样的东西。

在MySQLi和PDO之间做出选择时,有很多需要考虑的因素。事实上,除非你的请求数量非常高,否则这并不重要,在这种情况下,使用一个专门为MySQL设计的扩展比使用一个抽象的东西并恰好提供MySQL驱动程序更有意义。

这不是哪一个最好的简单问题,因为每一个都有优点和缺点。您需要阅读我提供的链接,然后做出自己的决定,然后测试并找出答案。我在过去的项目中使用过PDO,它是一个很好的扩展,但我对纯性能的选择是MySQLi与新的MySQLND选项编译(当PHP 5.3发布时)。

我不认为自己会很快从MySQL转换过来——所以我想我不需要PDO的抽象功能。DavidM,谢谢你的文章,它们帮了我很多。

谢谢你关于PHP缓存扩展的建议——你能解释一下为什么要使用一个而不是另一个吗?我听说过通过IRC的memcached很棒,但从来没有听说过APC -你对它们有什么看法?我认为使用多个缓存系统会适得其反。

实际上,很多人同时使用APC和memcached……

PDO也非常慢,而且它的API相当复杂。如果不考虑可移植性,任何头脑正常的人都不应该使用它。让我们面对现实吧,99%的网络应用都不是这样的。你只需坚持使用MySQL或PostrgreSQL,或任何你正在使用的。

至于PHP的问题和要考虑什么。我认为过早的优化是万恶之源。,)首先完成你的应用程序,在编程时尽量保持干净,做一点文档并编写单元测试。有了以上所有的方法,在必要的时候重构代码就没有问题了。但首先你想把它做完,然后把它推出去,看看人们对它有什么反应。

我运营的网站每月有700万到800万的访问量。不是特别多,但足以让我们的服务器感受到负载。我们选择的解决方案很简单:数据库级的Memcache。如果数据库负载是您的主要问题,则此解决方案效果很好。

我们开始使用Memcache缓存最常用的整个对象和数据库结果。它确实起作用了,但它也引入了bug(如果我们更加小心的话,我们可能会避免其中一些bug)。

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

现在,您所要做的就是决定查询是否可以使用缓存(可能已经过期)的结果。用户运行的大多数查询现在都直接从Memcache中获取。例外情况是更新和插入,这对于主网站来说只发生在日志记录中。这个相当简单的措施减少了大约80%的服务器负载。

无论如何,在PHP中缓存是非常简单的,即使没有像memcached这样的扩展/帮助包。

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

创建全局缓存函数。调用ob_start,将函数作为回调函数传递。在函数中,查找页面的缓存版本。如果存在,就把它送上,然后结束。

如果不存在,脚本将继续处理。当它到达匹配的ob_end()时,它将调用您指定的函数。这时,您只需要获取输出缓冲区的内容,将它们放到一个文件中,保存文件,然后结束。

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

许多人没有意识到可以嵌套ob_start()/ob_end()调用。因此,如果你已经在使用输出缓冲区,例如,在广告中解析或做语法高亮或其他,你可以嵌套另一个ob_start/ob_end调用。

查看mod_cache, Apache web服务器的输出缓存,类似于ASP.NET中的输出缓存。

是的,我知道它还在试验阶段,但总有一天会最终实现。

一般

  • 在开始看到真实世界的负载之前,不要尝试优化。你可能猜对了,但如果你猜错了,那你就是在浪费时间。
  • 使用jmeterxdebug或其他工具对站点进行基准测试。
  • 如果加载开始成为一个问题,对象或数据缓存都可能涉及到,所以通常阅读缓存选项(memcached, MySQL缓存选项)

代码

  • 对代码进行分析,以便了解瓶颈在哪里,以及它是在代码中还是在数据库中

数据库

  • 如果对其他数据库的可移植性不重要,则使用MYSQLi,否则使用PDO
  • 如果基准测试显示数据库是问题所在,请在开始缓存之前检查查询。使用解释来查看你的查询在哪里变慢了。
  • 在优化查询并以某种方式缓存数据库之后,您可能希望使用多个数据库。复制到多个服务器或分片(在多个数据库/服务器上分割数据)可能是合适的,这取决于数据、查询和读/写行为的类型。

缓存

  • 在缓存代码、对象和数据方面已经做了大量的编写。查找关于APCZend优化器memcachedQuickCacheJPCache的文章。在你真正需要之前做这些工作,你就不会那么担心没有优化的开始。
  • APC和Zend Optimizer是操作码缓存,它们通过避免代码的重新解析和重新编译来加速PHP代码。一般安装简单,值得早做。
  • Memcached是一种通用缓存,可以用于缓存查询、PHP函数或对象或整个页面。必须专门编写代码来使用它,如果没有中心点来处理缓存对象的创建、更新和删除,这可能是一个复杂的过程。
  • QuickCache和JPCache是文件缓存,在其他方面类似于Memcached。基本概念很简单,但也需要代码,并且更容易创建、更新和删除中心点。

杂项

  • 考虑高负载的替代web服务器。像lighthttpnginx这样的服务器可以用比Apache少得多的内存处理大量流量,如果你可以牺牲Apache的强大功能和灵活性(或者如果你不需要这些东西,通常情况下,你不需要)。
  • 请记住,现在的硬件非常便宜,所以一定要花费精力来优化一大块代码,而不是“让我们购买一个巨型服务器”。
  • 考虑将“MySQL”和“scaling”标签添加到这个问题中

我不敢相信居然没有人提到这个:模块化和抽象。如果你认为你的网站将不得不增长到许多机器,你必须设计它,使它可以!这意味着一些愚蠢的事情,比如不要假设数据库在本地主机上。它还意味着一些一开始会很麻烦的事情,比如编写数据库抽象层(像PDO,但要轻得多,因为它只做您需要它做的事情)。

这意味着在一个框架下工作。你需要对你的代码进行分层,这样你以后就可以通过重构数据抽象层来获得性能,例如,通过告诉它一些对象在不同的数据库——代码不需要知道也不需要关心

最后,要注意内存密集型操作,例如不必要的字符串复制。如果你能保持PHP的内存使用较低,那么你的web服务器就会得到更好的性能,当你采用负载平衡的解决方案时,这是可以扩展的。

已经给出了很多很好的答案,但我想给你指出一个名为XCache的备用操作码缓存。它是由一个轻量级贡献者创建的。

此外,如果将来你可能需要平衡数据库服务器的负载,MySQL代理可以很好地帮助你实现这一点。

这两种工具都可以很容易地插入到现有的应用程序中,因此可以在需要时进行优化,而不需要太多麻烦。

我是一个拥有超过1500万用户的网站的首席开发人员。我们很少遇到规模问题,因为我们很早就计划好了,并且经过深思熟虑。以下是我根据自己的经验提出的一些策略。

< >强模式 首先,去规范化您的模式。这意味着您不应该使用多个关系表,而应该选择使用一个大表。通常,连接会浪费宝贵的DB资源,因为多次准备和排序会消耗磁盘I/O。尽量避免使用。< / p >

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

< >强索引 确保您的查询使用了至少一个索引。但是要注意的是,如果频繁地编写或更新索引将会使您付出代价。有一些实验性的技巧可以避免这种情况

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

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

< >强缓存 我强烈推荐Memcached。它已经被PHP堆栈上最大的玩家(Facebook)证明了,而且非常灵活。有两种方法可以做到这一点,一种是缓存在你的DB层,另一种是缓存在你的业务逻辑层

DB层选项需要缓存从DB检索的查询结果。您可以使用md5()散列SQL查询,并在进入数据库之前将其用作查找键。这样做的好处是它很容易实现。缺点(取决于实现)是您失去了灵活性,因为您在缓存过期方面对所有缓存都一视同仁。

在我工作的车间中,我们使用业务层缓存,这意味着系统中的每个具体类都控制自己的缓存模式和缓存超时。这对我们来说工作得很好,但是要注意从DB中检索到的项可能与从缓存中检索到的项不一样,所以你必须同时更新缓存和DB。

< >强劲数据分片 复制只能让你到此为止。很快,写操作就会成为瓶颈。为了弥补这一点,请确保尽早支持数据分片。如果你不这样做,以后你可能会想开枪自杀。< / p >

它的实现非常简单。基本上,您希望将密钥权限与数据存储分离。使用全局DB存储主键和集群id之间的映射。您可以查询此映射以获得一个集群,然后查询集群以获得数据。您可以缓存这个查找操作,这将使它成为一个可以忽略不计的操作。

这样做的缺点是可能很难从多个碎片中拼凑出数据。但是,你也可以设计自己的方法。

< >强离线处理 不要让用户等待你的后端,如果他们没有必要的话。构建一个作业队列,并将任何处理移至离线,将其与用户的请求分开

如果您正在处理大量数据,而缓存无法解决问题,请查看Sphinx。我们使用SphinxSearch取得了很好的结果,不仅可以更好地进行文本搜索,还可以在处理较大的表时作为MySQL的数据检索替代品。如果你使用SphinxSE (MySQL插件),它所获得的性能收益超过了我们从缓存中获得的几倍,并且应用程序实现是一个麻烦。

关于缓存的观点是正确的;这是构建高效应用程序中最简单也是最重要的部分。我想补充的是,虽然memcached很棒,但如果您的应用程序位于单个服务器上,那么APC大约要快5倍。

MySQL性能博客上的“缓存性能比较”有一些关于这个主题的有趣的基准测试——http://www.mysqlperformanceblog.com/2006/08/09/cache-performance-comparison/

第一个问题是,你真正期望它有多大?你们计划在基础设施上投资多少?既然你觉得有必要在这里问这个问题,我猜你希望从有限的预算开始。

如果站点不可用,则性能无关紧要。为了获得可用性,你需要水平扩展。至少你可以使用2台服务器,同时运行apache, php和mysql。将一个DBMS设置为另一个DBMS的从属数据库。在主数据库上执行所有的写操作,在本地数据库上执行所有的读操作(不管本地数据库是什么)——除非出于某种原因需要回读刚刚读过的数据(使用主数据库)。确保你有适当的机制来自动提升奴隶和围栏主人。为web服务器地址使用轮询DNS,为从节点提供更多的亲和性。

在这个阶段,在不同的数据库节点上划分你的数据是一个非常糟糕的主意——然而,你可能会考虑在同一台服务器上的不同数据库上划分数据(当你超越facebook时,这将有助于跨节点划分)。

一定要确保你有适当的监控和数据分析工具来衡量你的网站性能和识别瓶颈。大多数性能问题都可以通过编写更好的SQL /修复数据库模式来解决。

将模板缓存保存在数据库上是一个愚蠢的想法——数据库应该是结构化数据的中央公共存储库。将你的模板缓存保存在你的web服务器的本地文件系统中——这样会更快,也不会降低你对数据库的访问速度。

使用操作码缓存。

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

将尽可能多的缓存推到客户端。

使用mod_gzip可以压缩所有内容。

C。

我的第一个建议是考虑这个问题,并在设计网站时牢记它,但不要走极端。通常很难预测一个新网站的成功,我认为你的时间最好花在早点起床,然后再优化它。

一般来说,简单就是快。 模板会降低您的速度。数据库会降低您的速度。复杂的库会降低您的速度。从数据库中检索模板并在一个复杂的库中解析它们——>时间延迟相互相乘

一旦你有了基本的站点并运行起来,做测试会告诉你在哪里花费你的精力。很难看出目标在哪里。通常,为了加快速度,你必须分解代码的复杂性,这会使代码变得更大,更难维护,所以你只在必要的时候才这么做。

根据我的经验,建立数据库连接是相对昂贵的。如果可以的话,不要在访问量最大的页面(如网站首页)上为普通访问者连接数据库。创建多个数据库连接非常疯狂,而且收效甚微。