假设有一个插件可以显示20个相关帖子(每个帖子)以及非常复杂的查询。然后使用该查询中的数据,构建复杂的HTML布局。另外,应注意,该插件是公共的,可以安装在具有任何配置的任何服务器上。

类似的东西:

/* complex and large query */
$related_posts = get_posts( ... );

$html_output = '';
foreach($related_posts as $key => $item) {
     /* complex layout rendering logic (but not as slow as the previous query) */   
     $html_output .= ...;
}


因此,我的问题是:


缓存此类数据的最安全,最正确的方法是什么?
我应该使用Transient API缓存$related_posts数组还是$html_output字符串?如果我将缓存$html_ouput字符串,它将达到某个最大大小限制吗?保存之前,我应该gzip压缩吗?
我应该在这里使用Transient API吗?


#1 楼


我应该在这里使用Transient API吗?


否。

在股票WordPress中,瞬态存储在wp_options表中,并且仅在核心升级期间清理。假设您有50,000个帖子,那么选项表中还有50,000个额外的行。显然,它们设置为autoload = no,因此不会消耗您的所有内存,但还有一个警告。

options表中的autoload字段没有索引,这意味着调用wp_load_alloptions()将执行全表扫描。您拥有的行越多,花费的时间就越长。写入选项表的次数越多,MySQL内部缓存的效率就越低。

如果缓存的数据与帖子直接相关,则最好将其存储在帖子元中。每次需要显示缓存的内容时,这也会为您节省查询,因为(通常)在WP_Query中的帖子检索期间,帖子元缓存已准备好。

您的元值数据结构可能会有所不同,如果缓存的值已过时,您可以设置一个时间戳并执行昂贵的查询,就像瞬态行为一样。

要记住的另一个重要思想是WordPress瞬态在环境中可能是易变的持久对象缓存。这意味着,如果您将缓存的数据临时存储24小时,则绝对不能保证它在23小时甚至12分钟甚至5分钟后就可用。许多安装的对象缓存后端是内存中的键值存储,例如Redis或Memcached,如果分配的内存不足以容纳较新的对象,则将驱逐较旧的对象。对于元存储方法而言,这是一个巨大的胜利。

失效也可以变得更聪明,即为什么要在X个小时内使相关帖子缓存失效?是否因为某些内容已更改?新增了一个帖子?是否分配了新标签?根据您的“复杂而庞大的查询”,您可以选择仅在发生某些事情会改变查询结果的情况下才使它无效。


我应该使用Transient API来缓存$ related_posts数组或$ html_output字符串?如果我将缓存$ html_ouput字符串,它将达到最大大小限制吗?


在保存之前我应该​​gzip压缩它吗?这很大程度上取决于字符串的大小,因为这是将在PHP,MySQL等之间流动的数据。我需要非常努力地达到MySQL的限制,但是例如Memcached的默认每个对象限制仅为1 mb。

您的“复杂布局呈现逻辑”实际上需要多长时间?通过分析器运行它以找出答案。很有可能,它永远不会成为瓶颈。

如果是这种情况,我建议缓存帖子ID。不是WP_Post对象,因为它们将包含完整的帖子内容,而只是一个帖子ID数组。然后只需使用带有WP_Querypost__in,这将导致通过主键进行非常快速的MySQL查询。

这就是说,如果每个项目所需的数据都非常简单,例如标题,缩略图url和永久链接,那么您可以只存储这三个变量,而无需额外花费往返MySQL的开销,也不需要缓存非常长的HTML字符串的开销。

哇,很多话,希望对您有所帮助。

#2 楼

并非所有WP代码都是公共代码

如果您要公开发布某些内容,那么kovshenin所说的所有内容都是完全正确的。

如果要发布的内容完全不同,为自己或您的公司编写私人代码。

在任何情况下,外部对象缓存都是一个很大的好处

建议您尽可能设置外部持久对象缓存。

kovshenin关于瞬态和MySQL的回答中所说的所有事情都是真实的,并且考虑到WP本身和许多插件都利用了对象缓存...那么您获得了性能提升,绝对值得(小的)精力来设置像Redis或Memcached这样的现代缓存系统。

缓存的值可能不存在:没关系。

此外,是的,外部对象缓存不可靠。您永远不应依赖瞬态存在的事实。您需要确保如果缓存不在应有的位置,它可以工作。

缓存不是存储,缓存是缓存。

选择性地使用缓存

请参见以下示例:

function my_get_some_value($key) {
   // by default no cache when debug and if no external object_cache
   $defUse = ! (defined('WP_DEBUG') && WP_DEBUG) && wp_using_ext_object_cache();
   // make the usage of cache filterable
   $useCache = apply_filters('my_use_cache', $defUse);
   // return cached value if any
   if ($useCache && ($cached = get_transient($key))) {
     return $cached;
   }
   // no cached value, make sure your code works with no cache
   $value = my_get_some_value_in_some_expensive_way();
   // set cache, if allowed
   $useCache and set_transient($key, $value, HOUR_IN_SECONDS);

   return $value;
}


使用这样的代码,在您的私有站点中,站点性能可以提高很多,特别是如果您有很多用户。 br />
请注意:



默认情况下,在打开调试时不使用缓存,因此希望在您的开发环境中使用。相信我,缓存会使调试变得很困难
默认情况下,当未将WP设置为不使用外部对象缓存时,也不使用缓存。这意味着与MySQL有关的所有问题都不存在,因为当他们使用MySQL时,您不会使用任何瞬态。可能更简单的替代方法是使用wp_cache_*函数,因此,如果未设置外部高速缓存,则该高速缓存会在内存中发生,并且永远不会涉及数据库。遇到

如果没有缓存则没有Webscale

您不应该尝试解决缓存的速度问题。如果您遇到速度问题,则应该重新考虑代码。

但是要在Webscale上扩展网站,非常需要缓存。

很多时候(但并非总是如此),上下文感知型缓存比主动式全页缓存更加灵活和适合。

您的问题:


我应该在以下位置使用Transient API吗?全部都在这里吗?


这取决于。

您的代码是否消耗大量资源?如果没有,也许不需要缓存。
如上所述,不仅仅是速度问题。如果您的代码运行速度很快,但需要几个用户使用大量CPU和内存...当您有100个或1000个并发用户时会发生什么?

如果您意识到缓存是个好主意..

...并且是公共代码:可能不行。您可以考虑选择性地缓存,就像上面的示例中的公共代码一样,但是如果将这样的决定留给实现者,通常会更好。

...并且是私有代码:很可能是。但是,即使对于私有代码,选择性地缓存仍然是一件好事,例如对于调试而言。

无论如何,请记住,wp_cache_*函数可以使您访问缓存而不会污染数据库。


我应该使用Transient API来缓存$ related_posts数组或$ html_output字符串吗?


它取决于很多事情。弦多大?您正在使用哪个外部缓存?如果要缓存帖子,将ID存储为数组可能是个好主意,通过它们的ID查询相当数量的帖子是非常快的。

最后的注释

瞬态API可能是WordPress最好的东西之一。多亏了可以在任何类型的缓存系统中找到的插件,它成为许多可以在后台运行的软件的愚蠢的简单API。

在WordPress之外,很难找到这样的抽象,它可以与一堆不同的缓存系统一起使用,并且可以轻松地从一个系统切换到另一个系统。

您很少能听到我说WordPress比其他现代功能要好,但是当我不使用WordPress时,瞬态API就是我想念的少数东西之一。

当然缓存很难,不能解决代码问题并不是灵丹妙药,但是它是构建一个高流量站点的有效方法。

使用未充分优化的MySQL表进行缓存的WordPress想法相当疯狂,但是最好不要因为WordPress默认情况下就这么做而使自己远离缓存。

您只需要了解事物的工作原理,然后做出选择即可。

#3 楼

先前的答案已经强调了我完全同意的强制性“取决于”。

但是我想添加一条建议,基于我“假设”的方式,最好在

在这种情况下,我不会使用Transients,而应该使用Post Meta,因为后者具有以下优势:控制。

由于需要按每个帖子缓存数据,因此要缓存的数据量取决于发布的数量,并且会随着时间增长。一旦超过一定数量的帖子,您可能会达到对象缓存允许使用的内存限制,并且它将在过期之前开始从内存中删除先前缓存的数据。这可能会导致您的访问者大量涌入,每个访问者都会在每个页面请求时触发“过于复杂的SQL”,并且您的站点将完全陷入困境。

如果您将数据缓存在Post Meta中,不仅可以控制数据的存储和检索方式,还可以精确地控制数据的更新方式。您将为此添加一个cron作业,该作业仅在网站流量很少甚至没有流量的时间段运行。因此,网站的真正用户永远不会遇到“慢查询”,您甚至可以预先加载它,以便在第一个访问者访问时就已经完成了工作。

所有缓存都是一个折衷方案!这就是为什么通常的答案是“取决于情况”的原因。以及为什么没有“圣杯”。