在高负载站点中使用 PHP 的策略数据库缓存

2022-08-30 06:10:52

在你回答这个问题之前,我从未开发过任何足够流行的东西来获得高服务器负载。把我当成一个刚刚登陆地球的外星人,尽管他知道PHP和一些优化技术。


我正在PHP中开发一个工具,如果工作正常,它可以吸引相当多的用户。然而,虽然我完全有能力开发程序,但在制作可以处理巨大流量的东西时,我几乎无能为力。所以这里有一些问题(也可以随意把这个问题变成一个资源线程)。

数据库

目前,我计划在 PHP5 中使用 MySQLi 功能。但是,我应该如何设置与用户和内容相关的数据库?我是否真的需要多个数据库?目前,所有内容都混杂在一个数据库中 - 尽管我一直在考虑将用户数据传播给一个数据库,将实际内容传播到另一个数据库,最后将核心网站内容(模板母版等)传播到另一个数据库。我的理由是,将查询发送到不同的数据库将减轻它们的负载,因为一个数据库= 3个加载源。另外,如果它们都在同一台服务器上,这仍然有效吗?

缓存

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

我对此提出问题:

假设我对不同的文章有评论。哪个是更好的解决方案:存储简单的注释模板并在每次加载页面时呈现注释(来自数据库调用),或者将注释页面的缓存副本存储为html页面 - 每次添加/编辑/删除注释时,页面都会重新缓存。

最后

有没有人有任何提示/指针在PHP上运行高负载站点。我很确定这是一种可行的语言 - Facebook和Yahoo!给了它很大的优先权 - 但是有什么我应该注意的经验吗?


答案 1

没有两个网站是相同的。你真的需要得到一个像jmeter和基准测试这样的工具,看看你的问题点在哪里。你可以花很多时间猜测和改进,但在你衡量和比较你的变化之前,你不会看到真正的结果。

例如,多年来,MySQL查询缓存是我们所有性能问题的解决方案。如果您的网站速度很慢,MySQL专家建议打开查询缓存。事实证明,如果您的写入负载很高,则缓存实际上是瘫痪的。如果你在没有测试的情况下打开它,你永远不会知道。

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

数据库

  • 不要使用MySQLi - PDO是“现代”OO数据库访问层。要使用的最重要的功能是查询中的占位符。它足够智能,可以为您使用服务器端准备和其他优化。
  • 此时,您可能不想分解数据库。如果你确实发现一个数据库没有切割,则有几种技术可以纵向扩展,具体取决于你的应用。如果读取次数多于写入次数,则复制到其他服务器通常效果很好。分片是一种将数据拆分到多台计算机上的技术。

缓存

  • 您可能不希望在数据库中缓存。数据库通常是您的瓶颈,因此向其添加更多 IO 通常是一件坏事。有几种PHP缓存可以完成类似的事情,如APC和Zend。
  • 在打开和关闭缓存的情况下测量系统。我敢打赌你的缓存比直接提供页面更重。
  • 如果从数据库构建评论和文章数据需要很长时间,请将 memcache 集成到您的系统中。您可以缓存查询结果并将其存储在 memcached 实例中。重要的是要记住,从memcache检索数据必须比从数据库中组装数据更快,才能看到任何好处。
  • 如果你的文章不是动态的,或者你在生成后有简单的动态变化,请考虑将html或php写到磁盘上。你可以有一个索引.php页面,在磁盘上查找文章,如果它在那里,它会将其流式传输到客户端。如果不是,它将生成文章,将其写入磁盘并将其发送到客户端。从磁盘中删除文件将导致页面被重写。如果将注释添加到文章中,请删除缓存的副本 - 它将被重新生成。

答案 2

我是一个拥有超过1500万用户的网站的首席开发人员。我们几乎没有遇到过扩展问题,因为我们很早就计划好了,并进行了深思熟虑的扩展。以下是我可以根据自己的经验建议的一些策略。

图式首先,对架构进行非规范化。这意味着,与其拥有多个关系表,不如选择一个大表。通常,联接会浪费宝贵的数据库资源,因为执行多次准备和排序会烧毁磁盘 I/O。尽可能避免使用它们。

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

索引请确保您的查询至少使用一个索引。但请注意,如果您经常写入或更新索引,索引将花费您。有一些实验技巧可以避免这种情况。

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

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

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

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

在我工作的车间中,我们使用业务层缓存,这意味着我们系统中的每个具体类都控制自己的缓存架构和缓存超时。这对我们来说效果很好,但请注意,从数据库检索到的项目可能与从缓存中检索到的项目不同,因此您必须同时更新缓存和数据库。

数据分片复制只能让您走得更远。比您预期的要快,您的写入将成为瓶颈。为了弥补这一点,请确保尽早支持数据分片。如果你不这样做,你可能会想稍后开枪自杀。

它实现起来非常简单。基本上,您希望将密钥颁发机构与数据存储分开。使用全局数据库存储主键和集群 ID 之间的映射。查询此映射以获取群集,然后查询群集以获取数据。您可以缓存此查找操作的地狱,这将使其成为一个可以忽略不计的操作。

这样做的缺点是可能很难将来自多个分片的数据拼凑在一起。但是,您也可以通过设计自己的方式。

离线处理如果用户不必等待您的后端,请不要让用户等待。构建作业队列并移动任何可以脱机的处理,将其与用户的请求分开执行。


推荐