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


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

数据库

At the moment I plan to use the MySQLi features in PHP5. However how should I setup the databases in relation to users and content? Do I actually need multiple databases? At the moment everything's jumbled into one database - although I've been considering spreading user data to one, actual content to another and finally core site content (template masters etc.) to another. My reasoning behind this is that sending queries to different databases will ease up the load on them as one database = 3 load sources. Also would this still be effective if they were all on the same server?

缓存

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

我的问题是:

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

最后

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


当前回答

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

其他回答

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

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

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

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

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

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

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

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

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

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

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

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

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

回复: PDO / MySQLi / MySQLND

@gary

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

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

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

@ross

You are correct about MySQLnd which is the newest MySQL core language level library, however it is not a replacement for MySQLi. MySQLi (as with PDO) remains the way you would interact with MySQL through your PHP code. Both of these use libmysql as the C client behind the PHP code. The problem is that libmysql is outside of the core PHP engine and that is where mysqlnd comes in i.e. it is a Native Driver which makes use of the core PHP internals to maximise efficiency, specifically where memory usage is concerned.

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

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

这里可以找到一些有用的基准,尽管它们是2006年的。你还需要注意类似这个选项的事情。

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

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

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

Performance is irrelevant if the site is not available. And for availability you need horizontal scaling. The minimum you can sensibly get away with is 2 servers, both running apache, php and mysql. Set up one DBMS as a slave to the other. Do all the writes on the master, and all the reads on the local database (whatever that is) - unless for some reason you need to read back the data you've just read (use master). Make sure you've got the machinery in place to automatically promote the slave and fence the master. Use round-robin DNS for the webserver addresses to give more affinity for the slave node.

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

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

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

使用操作码缓存。

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

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

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

C.

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

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

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

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

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

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