
WordPress 的核心缓存概念
缓存类型
首先,本节将涵盖人们可能遇到的四种不同类型的缓存。了解这些概念有助于浏览阅读缓存系统时使用的一些专业术语。
运行时缓存
运行时缓存是仅持续请求持续时间的缓存。对象存储在内存中,但请求完成后立即被驱逐。每当您将例程的值或结果设置为变量并多次使用它时,您都在使用运行时缓存。如果您在一次请求中需要两次相同的数据,则多次重新生成数据是没有意义的。
作为 WordPress 的例子,主查询和当前文章对象分别存储在 $wp_query 和 $post 全局变量中。当需要有关当前文章的数据时,不会再次查询 MySQL;相反,数据是从 $post 全局变量中提取的。运行时缓存是一种简单高效的数据缓存策略。
运行时缓存的主要问题是它只能持续请求的持续时间。请求一完成,缓存就会被转储。即使访问者将生成可能用于多个请求的数据,但该数据不会在请求中持续存在,并且需要为每个请求重新生成。这个问题可以通过对象缓存来解决。
对象缓存
对象缓存是将数据从一个昂贵且检索缓慢的地方移动到一个便宜且快速检索的地方的行为。 对象缓存通常也是持久的,这意味着在一次请求期间缓存的数据在后续请求中可用。
除了使数据访问更容易之外,缓存的数据应该始终是可替换和可再生的。 如果应用程序遇到数据库损坏(例如 MySQL、Postgres、Couchbase),该数据库将且应该产生严重的后果(让我们希望有一个良好的备份计划)。 与应用程序的主数据存储相反,如果缓存损坏,应用程序应继续运行,因为缓存的数据应该自行再生。 不会丢失任何数据,尽管在缓存重新生成时可能会出现一些性能问题。
对象缓存的存储引擎可以是多种技术。 流行的对象缓存引擎包括 Memcached、APC、Redis 和文件系统。 使用的缓存引擎应根据应用程序的需求来决定。 每一种都有其优点和缺点。 至少,使用的引擎应该比重新生成数据更能访问数据。
对象缓存对应用程序来说往往非常重要,因为它可用于实现本文将要讨论的其他缓存。 换句话说,如果您的对象缓存实现不正确,您可能会破坏缓存架构的其余部分。
页面缓存
页面缓存存储表示单个页面的 HTML 数据。在许多情况下,页面使用对象缓存来存储其数据。在这种情况下,页面缓存只是一种特殊类型的对象缓存。也就是说,页面缓存可以使用与对象缓存完全不同的存储引擎。事实上,页面缓存的两种流行选择是 Varnish 和 Nginx,它们是页面缓存的反向代理实现,将数据与对象缓存分开存储。与可用于页面缓存存储的对象缓存引擎不同,反向代理缓存不是对象缓存存储引擎的良好候选者(并且将反向代理缓存用于对象缓存也存在一些重大技术限制)。
区分对象和页面缓存很重要。页面缓存可以以最小的努力为网站带来显著的性能提升;然而,它们受到限制,因为许多页面缓存系统假设每个页面对每个访问者都是相同的渲染。换句话说,人们通常假设该页面对个人用户来说永远不会是唯一的。如果您的网站符合这一要求,您将从实施页面缓存中获得重大收益。当引入对独特页面视图的需求时,页面缓存变得非常棘手(而且几乎不可能)。对于每个访问者都有独特的页面,有效使用对象缓存至关重要,但您可能不会从仅从对象缓存中看到与仅从页面缓存中看到的相同收益。
重要的是要记住,除了一些例外,当您实施页面缓存时,每个用户都会看到相同的页面。如果您使用为个人唯一呈现的数据开发网站(例如,在标题中输出用户名),则该数据将为所有用户缓存。当然有办法绕过这个问题(例如,不要缓存登录的视图),在实施页面缓存时,您必须考虑这一点。
片段缓存
片段缓存是只缓存整页的一部分的行为。片段只是不是整页的对象。区分物体和碎片可能真的很困难(我发现这是一个基本没有结果的努力)。当人们谈论片段时,他们通常指的是可识别的页面区块。例如,个人资料小工具、页脚或相关文章列表都将被视为片段(但是,呃,它们也是对象)。
通常,片段缓存使用对象缓存作为片段的存储引擎。从这个意义上说,片段缓存通常只不过是存储页面命名部分的对象缓存。
就我个人而言,我不喜欢过多地谈论片段缓存,因为它往往比将片段视为对象没有优势。我在这里提到它,因为这是一个人们经常使用的术语。在我看来,最好将片段缓存视为对象缓存的同义词。
缓存隐喻
由于本文的主要目的是理解上面介绍的缓存概念,我将介绍一个隐喻来增强对这些概念的理解,特别强调页面和对象缓存。 我之前说过,我们都有缓存和我介绍的概念的经验。 让我解释一下。
缓存就像购买和储存杂货一样。 当你去商店时,你会购买各种各样的物品。 从商店回来后,你把物品存放在橱柜、冰箱和柜台上。 你给商店的小费是一种缓存行为。 从一个地方获取食物,并将其储存在一个新的地方,这样可以更便宜、更快地获得,遵循相同的原则。 让我们将特定食品与缓存进行比较。
我们很多人买鸡蛋用于不同的饭菜。 购买鸡蛋的一个策略是去一个单独出售鸡蛋的市场。 如果你早上醒来想吃2个鸡蛋煎蛋卷,你可以走到市场,买2个鸡蛋,然后回家做煎蛋卷。 如果你想在第二天的早餐里吃煎蛋卷,你可以重复这个过程。 你们中的大多数人会认为这是一个相当荒谬的过程,并会立即看到一个更有效的策略,即在一次市场旅行中购买一打鸡蛋,然后将它们存放在冰箱里,以便在做早餐时快速取用。
购买鸡蛋并将其存放在冰箱中的过程类似于网络开发中的缓存对象。 这个过程相似,因为您将资源从一个难以访问的地方移动到一个容易访问的地方。
为了进一步改进这个比喻,去商店的一个目的是获得食材来做饭。 一顿饭由多种食材组成。 如果你的冰箱或橱柜里有食材,你可以用这些食材来制作这顿饭。 在这种情况下,餐食类似于页面缓存。 页面缓存由许多组件组成,其中一些是缓存项目。 做饭时,你从冰箱或柜子里拿出物品,如果你缺少一些物品,你必须去商店。 然后,这顿饭由你家里和商店里找到的物品组成。 如果你真的很有效率,那么这顿饭完全由你家里发现的物品组成。 这与页面缓存相似,如果您的应用程序利用对象缓存,您的页面缓存可以完全由缓存对象组成,这些对象被拉在一起形成单个页面视图。
有时,当我们做饭时,我们也可以非常高效地做额外的工作。 也许在煮煎蛋卷时,你决定煮5个煎蛋卷,储存以供以后食用。 这类似于页面缓存,因为您将构建页面缓存并将其存储为对象以供以后使用。 与其做5次煎蛋卷,不如一次做5个煎蛋卷,然后把它们存放在冰箱里待饭。
但那里没有进行比较。 对象缓存可以通过各种存储引擎实现,就像您可以将食物存储在各种存储设备中一样。 你可以把你的货放在冰箱、冰柜、柜子、柜台、架子上、储藏室、地下室等地方。 您根据您拥有的存储选项、存储设备的容量、您对其他存储设备的访问等做出这些决定。 在为您的应用程序决定存储引擎时,您会考虑这些相同的决定。 就像把鸡蛋放在冰箱里而不是放在橱柜里一样,用 Varnish 而不是 Memcached 保存你的页面缓存可能更有意义;然而,有时你没有冰箱可以支配,你必须即兴发挥,并将鸡蛋存放在装满冰块的冷却器中。
使用隐喻
这个隐喻的目的是清楚地表明你对缓存的了解比你想象的要多。 您以前使用过缓存策略。 您可以通过将网络开发中的缓存与检索杂货准备饭菜的过程进行比较来获得很多里程。
例如,你会嘲笑一个人,每次吃饭需要一杯面粉时,他都会去商店买一杯面粉。 你会立即意识到这个策略耗时、效率低下且成本高昂。 作为网络开发人员,当开发人员每次加载页面时 ping Twitter 的 API 以获取推文时,您应该有完全相同的反应。 这是一个昂贵的过程,速度缓慢且效率低下。 在这两种情况下,您都应该认识到获取您需要的对象并将其存放在更容易访问以备将来使用的地方的重要性。
请记住,对象缓存就像杂货店购物一样。 存放获得的物品就像把家里的杂货收起来一样。 构建页面缓存就是用缓存的项目制作餐食。 通过将您的应用程序视为做饭的类比,您可以深入了解缓存策略中的低效率。
将缓存概念应用于 WordPress
现在我们已经很好地了解了核心缓存概念,是时候将它们应用于 WordPress 了。 在本文的这一部分,我将重点关注对象缓存和页面缓存,因为 WordPress 中与这些概念有明显的相关性。 WordPress 中没有片段缓存,所以不会讨论。
WordPress 对象缓存
WordPress 通过两种不同的机制实现对象缓存:瞬态和 WP_Object_Cache 类。 对象缓存的标志是提供一个持久的缓存后端,允许缓存数据跨请求可用。 瞬态和 WP_Object_Cache 类都可以提供这种持久性。
瞬态
开箱即用,WordPress 支持通过瞬态进行持久对象缓存。 WordPress 瞬态 API 允许您从数据库中存储、检索和删除对象。 默认情况下,瞬态缓存使用 wp_options 表进行数据存储。 关于瞬态作为对象缓存的一个困惑点通常来自于瞬态存储在 WordPress 的 MySQL 数据库表中的事实。 MySQL 数据库是存储对象的最小足够地方,因为它可以是一个比对象的原始位置更快地检索数据的位置。
瞬态是 WordPress 中一个很好的对象缓存选项,因为它们提供了一个零配置的持久缓存。 对于插件和主题开发人员,您几乎可以保证在使用瞬态 API 时将使用持久缓存。 瞬态缓存的缺点是它使用的是 MySQL,这是存储缓存数据的速度较慢、风险更高的选项之一。 通常,将缓存引擎与主数据存储分开是更好的策略,以最大限度地提高两个存储的效率。
WP_Object_Cache 类
WP_Object_Cache 类是一个定义 WordPress 对象缓存存储引擎的类。 该类可以用自定义类覆盖,这意味着开发人员可以配置 WordPress,以使用任何存储引擎作为对象缓存。 WordPress 世界中最受欢迎的两个是 Memcached 和 APC。
使用 WP_Object_Cache 类的优势主要在于性能。 使用此类,您可以扩展 WordPress,以使用世界上最好的缓存引擎。 例如,使用 Memcached 作为 WordPress 的对象缓存提供了非常快的数据访问,可以轻松扩展到多个服务器。 Memcached 是一个重要的缓存引擎,用于高流量网站。 使用 WP_Object_Cache 类,开发人员可以微调 WordPress 中的缓存体验,而使用瞬态 API 对缓存引擎的控制很少。 将这个类与隐喻相关,WP_Object_Cache 类允许您精确决定将食物存放在哪里。
作为使用 WP_Object_Cache 类的额外好处,使用瞬态 API 的代码实际上将使用 WP_Object_Cache 类中的存储引擎(如果定义)。 例如,如果您有一个使用 Memcached 作为缓存引擎的微调系统,并且您安装了一个使用瞬态 API 的插件,它将充分利用您的 Memcached 安装,而不是将数据存储在 MySQL 数据库中。
默认情况下,WP_Object_Cache 被定义,但只实现运行时缓存。 由于 WordPress 无法决定您是否有一个可用的存储引擎,并且因为它必须确保使用对象缓存 API 不会导致致命错误,因此它实现了默认的 WP_Object_Cache 类。 此默认类仅在运行时将数据存储在 PHP 变量中,并且是非持久的。 然而,这可以被覆盖。
要定义您自己的对象缓存,您必须将文件添加到 wp-content/object-cache.php 中。 如果定义了此文件,它将被加载,而不是默认类。 这种类型的文件被称为 WordPress drop-in。 不同缓存引擎的插件存储庫中存在一些不同的 object-cache.php 文件(例如 Memcached,APC)。
WordPress 页面缓存
与 WP_Object_Cache 类类似,WordPress 提供了一个下拉式来定义页面缓存。 通过将名为 advanced-cache.php 的文件放入 wp-content/ 目录中,您可以定义与缓存页面相关的所有逻辑。
页面缓存机制背后的逻辑的一般想法如下:
根据请求的 URL(以及其他一些信息),查看对象缓存,看看页面的缓存版本是否存在
如果页面存在,请提供它并完成请求
如果页面不存在,开始输出缓冲,加载页面,完成输出缓冲,并存储输出以供后续请求使用
页面缓存系统有一些更精细的细微差别,但该逻辑定义了生成页面缓存的主要机制。
advanced-cache.php 的美妙之处在于它在 WordPress 页面加载的前1% 中加载。 因此,如果找到缓存页面,99%的WordPress 负载被避免,这导致应用程序的性能显著提升。 Advanced-cache.php 需要注意的一件重要事情是,它需要一个持久的缓存来存储其数据。 最有效的解决方案是使用持久对象缓存作为数据存储,但存在将文件系统用于此缓存的可靠解决方案。
虽然使用 advanced-cache.php 是 WordPress 中最简单、最容易访问的页面缓存形式,但您也可以使用 Varnish、Nginx 或托管缓存解决方案的反向代理方法。 我不会在这里研究这些解决方案,因为这些解决方案大多是在系统级别配置的,与 WordPress 没有特别关系。 我只想把它作为 advanced-cache.php 的替代方案。
缓存提示
既然您对 WordPress 缓存有了一点了解,我想分享一些我通过 WordPress 缓存经验收集的“智慧”。 我希望你能避免我面临的一些挣扎。
永远不要依赖您的缓存来获取应用程序功能。 您应该始终在开发应用程序时假设缓存是100%损坏的。 无论缓存是否正常运行,您的应用程序仍应提供预期功能。 如果缓存损坏,您的缓存应该始终能够自行再生。
将您的缓存用作性能的“渐进式增强”。 虽然您的应用程序应该在没有缓存的情况下运行,但这并不意味着它应该表现良好。 缓存层是为了提供更好的性能,而不是功能。 因此,您可以将缓存层视为渐进式增强,可以提高应用程序的性能,但在没有缓存的情况下仍能工作。
通过打开和关闭缓存来测试您的应用程序。 为了验证您的应用程序是否正常工作,应在打开缓存和不打开缓存的情况下对其进行测试。 根据缓存策略和使用的存储引擎,这可能或多或少地困难。 为了避免真正不方便的惊喜,最好在两个州检查申请。
始终为缓存的每个对象设置过期值。 从理论上讲,为了获得最大的效率,您只应在缓存对象更改时刷新它;但是,如果您不将缓存对象设置为最终过期,您最终会遇到非常难以调试的情况。 这将有助于避免提供过时数据的问题。
了解您正在缓存的系统。 只有当环境满足某些要求时,才能使用不同的缓存策略。 在构建应用程序之前,最好尽可能多地了解环境。
在这篇文章中,我讨论了人们必须理解的基本概念,以便能够将缓存应用于网络开发项目。 我希望有了这些信息,您可以更好地准备在您的项目中进行缓存。 本文旨在作为入门书,使其更容易理解更复杂的缓存概念。