类似的东西:
/* 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_Query
的post__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作业,该作业仅在网站流量很少甚至没有流量的时间段运行。因此,网站的真正用户永远不会遇到“慢查询”,您甚至可以预先加载它,以便在第一个访问者访问时就已经完成了工作。
所有缓存都是一个折衷方案!这就是为什么通常的答案是“取决于情况”的原因。以及为什么没有“圣杯”。