对象缓存如何工作?


21

我在这里寻找一个明确的答案。启用对象缓存后,选项和瞬态在哪里结束?

默认情况下,两者都存储在数据库中。但是我听说一些参考资料称Memcache会将它们存储在其他位置,而APC将完全做其他事情。在这两种情况下,这些数据究竟将保留在哪里?


2
@toscho提到的文章现在可以在archive.org上找到:探索WordPress缓存API
在这里,

Answers:


34

WordPress默认情况下会执行“对象缓存”的形式,但其生存期仅为一次页面加载。

选项实际上是一个很好的例子。查看此答案以获取更多信息。摘要:

  1. 页面开始
  2. 所有选项都加载了一个简单的SELECT option_name, option_value from $wpdb->options语句
  3. 对这些选项的后续请求(例如,get_option从不打入数据库的调用,因为它们与WP缓存API一起存储。

选项始终“活跃”在数据库中,并且始终存在于数据库中-这就是它们的“规范”来源。就是说,选项已加载到对象缓存中,因此当您请求选项时,有99%的可能性该请求将永远不会到达数据库。

瞬态有些不同。

WordPress允许你用替换缓存API 插入式 -一个文件,该文件被直接放在你的wp-content文件夹中。如果您创建自己的缓存放入或使用现有的 插件,则可以使对象缓存的持续时间长于单个页面加载的时间。当您这样做时,瞬变会有所变化。

让我们看一下中的set_transient函数wp-includes/option.php

<?php
/**
 * Set/update the value of a transient.
 *
 * You do not need to serialize values. If the value needs to be serialized, then
 * it will be serialized before it is set.
 *
 * @since 2.8.0
 * @package WordPress
 * @subpackage Transient
 *
 * @uses apply_filters() Calls 'pre_set_transient_$transient' hook to allow overwriting the
 *  transient value to be stored.
 * @uses do_action() Calls 'set_transient_$transient' and 'setted_transient' hooks on success.
 *
 * @param string $transient Transient name. Expected to not be SQL-escaped.
 * @param mixed $value Transient value. Expected to not be SQL-escaped.
 * @param int $expiration Time until expiration in seconds, default 0
 * @return bool False if value was not set and true if value was set.
 */
function set_transient( $transient, $value, $expiration = 0 ) {
    global $_wp_using_ext_object_cache;

    $value = apply_filters( 'pre_set_transient_' . $transient, $value );

    if ( $_wp_using_ext_object_cache ) {
        $result = wp_cache_set( $transient, $value, 'transient', $expiration );
    } else {
        $transient_timeout = '_transient_timeout_' . $transient;
        $transient = '_transient_' . $transient;
        if ( false === get_option( $transient ) ) {
            $autoload = 'yes';
            if ( $expiration ) {
                $autoload = 'no';
                add_option( $transient_timeout, time() + $expiration, '', 'no' );
            }
            $result = add_option( $transient, $value, '', $autoload );
        } else {
            if ( $expiration )
                update_option( $transient_timeout, time() + $expiration );
            $result = update_option( $transient, $value );
        }
    }
    if ( $result ) {
        do_action( 'set_transient_' . $transient );
        do_action( 'setted_transient', $transient );
    }
    return $result;
}

$_wp_using_ext_object_cache?如果是真的,WordPress将使用对象缓存而不是数据库来存储瞬态。那么如何将其设置为true?是时候探讨WP如何设置自己的缓存API。

您可以追踪到几乎所有内容- wp-load.phpwp-settings.php两者都对WordPress的引导过程至关重要。在我们的缓存中,中有一些相关的行wp-settings.php

// Start the WordPress object cache, or an external object cache if the drop-in is present.
wp_start_object_cache();

还记得从上面掉下来的东西吗?让我们来看看wp_start_object_cachewp-includes/load.php

<?php
/**
 * Starts the WordPress object cache.
 *
 * If an object-cache.php file exists in the wp-content directory,
 * it uses that drop-in as an external object cache.
 *
 * @access private
 * @since 3.0.0
 */
function wp_start_object_cache() {
    global $_wp_using_ext_object_cache, $blog_id;

    $first_init = false;
    if ( ! function_exists( 'wp_cache_init' ) ) {
        if ( file_exists( WP_CONTENT_DIR . '/object-cache.php' ) ) {
            require_once ( WP_CONTENT_DIR . '/object-cache.php' );
            $_wp_using_ext_object_cache = true;
        } else {
            require_once ( ABSPATH . WPINC . '/cache.php' );
            $_wp_using_ext_object_cache = false;
        }
        $first_init = true;
    } else if ( !$_wp_using_ext_object_cache && file_exists( WP_CONTENT_DIR . '/object-cache.php' ) ) {
        // Sometimes advanced-cache.php can load object-cache.php before it is loaded here.
        // This breaks the function_exists check above and can result in $_wp_using_ext_object_cache
        // being set incorrectly. Double check if an external cache exists.
        $_wp_using_ext_object_cache = true;
    }

    // If cache supports reset, reset instead of init if already initialized.
    // Reset signals to the cache that global IDs have changed and it may need to update keys
    // and cleanup caches.
    if ( ! $first_init && function_exists( 'wp_cache_switch_to_blog' ) )
        wp_cache_switch_to_blog( $blog_id );
    else
        wp_cache_init();

    if ( function_exists( 'wp_cache_add_global_groups' ) ) {
        wp_cache_add_global_groups( array( 'users', 'userlogins', 'usermeta', 'user_meta', 'site-transient', 'site-options', 'site-lookup', 'blog-lookup', 'blog-details', 'rss', 'global-posts', 'blog-id-cache' ) );
        wp_cache_add_non_persistent_groups( array( 'comment', 'counts', 'plugins' ) );
    }
}

函数的相关行(与之相关的行$_wp_using_ext_object_cache更改了瞬态的存储方式)。

if ( file_exists( WP_CONTENT_DIR . '/object-cache.php' ) ) {
    require_once ( WP_CONTENT_DIR . '/object-cache.php' );
    $_wp_using_ext_object_cache = true;
} else {
    require_once ( ABSPATH . WPINC . '/cache.php' );
    $_wp_using_ext_object_cache = false;
}

如果object-cache.php内容目录中存在它,则将其包括在内,并且WP假设您使用的是外部持久性缓存-设置$_wp_using_ext_object_cache为true。

如果您使用的是外部对象缓存,则瞬态将使用它。这就提出了何时使用期权与瞬态的问题。

简单。如果需要数据无限期地保留,请使用选项。他们被“缓存”,但是他们的规范来源是数据库,除非用户明确要求,否则它们永远不会消失。

对于应存储一定时间但不需要在指定的生存期内保持不变的数据,请使用瞬态。在内部,如果可能的话,WP将尝试使用外部的持久对象缓存,否则数据将进入选项表,并在它们过期时通过WordPress的psuedo-cron收集垃圾。

其他一些关注/问题:

  1. 可以拨打大量电话get_option吗?大概。他们招致了函数开销的调用,但是它可能不会访问数据库。与您选择的语言生成页面所做的工作相比,数据库负载通常是Web应用程序可伸缩性中更大的关注点。
  2. 我如何知道使用瞬态与Cache API?如果您希望数据在设定的时间内持续存在,请使用瞬时API。如果数据是否持久无关紧要(例如,计算/获取数据不会花费很长时间,但每个页面加载不应发生多次),请使用缓存API。
  3. 所有选项真的都缓存在每个页面加载上吗?不必要。如果add_option使用其最后一个可选参数进行调用,因为no它们不会自动加载。也就是说,一旦提取它们一次,它们就会进入缓存,后续调用将不会到达数据库。

nitpick 1:并非所有选项都在页面开始时加载,只有那些在创建时标记为“ autoload = yes”的选项。add_option中该参数的默认值为“是”,并且大多数插件编写者都不会理会使用“否”的区别,在那里您的陈述实际上是正确的。
Mark Kaplun

即使非自动加载的选项被提取一次之后也会被缓存。它们可能最初并未加载,但之后确实会进入对象缓存。甚至不存在的选项也会被缓存!github.com/WordPress/WordPress/blob/master/wp-includes/…但是,我添加了有关自动加载选项的注释。
chrisguitarguy

那是nitpick 2;)
Mark Kaplun

感谢您的精彩文章,以及所有这些内容的总结。
prosti

5

我知道有4种缓存类型

  1. 琐碎-它始终处于启用状态,并在其他任何缓存起作用之前起作用。它将缓存的项目存储在php数组中,这意味着它会占用php执行会话中的内存,并且在php执行结束后清空缓存。即,即使不使用任何其他缓存,如果您连续两次调用get_option('opt'),也只会在第一次和第二次从内存返回值时进行数据库查询。

  2. 文件-缓存的值存储在根目录下某个位置的文件中。我相信,除非您拥有非常快的磁盘或内存映射文件存储,否则就性能而言,它是无效的。

  3. APC(或其他基于php加速器的缓存)-缓存的值存储在主机的内存中,而不在php内存分配中。最大的潜在陷阱是没有数据作用域,如果您运行两个站点,则每个站点都可能访问另一个站点的缓存数据或将其覆盖。

  4. Memcache-它是基于网络的缓存。您可以在网络上的任何地方运行缓存服务,它可能会将值存储在其主机内存中。除非您具有负载平衡功能,否则您可能不需要内存缓存。

顺便说一句,对象缓存比选项缓存要多得多,它几乎可以存储使用高级WP API从数据库检索到的所有内容。


我知道答案是相当古老的,但我还要添加出色的Redis
Cranio

@Cranio,您是对的,但是... redis本质上是带有存储的内存缓存的变体,因此它是(NoSQL)DB。这个IMHO实际上很糟糕,好像该节点发生故障或无法更新时,您可能会从中获取陈旧的信息。它具有关闭类似数据库行为的选项,但是我不确定默认情况下它是打开还是关闭。
马克·卡普伦

它是Memcached(甚至更好)的完美替代品,您还需要什么?到目前为止,我所见过的最普通的用法就是将其作为RAM键值存储(是的,除此之外,可以使数据持久化,正在集群化并且具有队列管理功能,但是每个人都认为Redis是一个出色的解决方案) WP的缓存选项)
Cranio

每个人也都可以跳桥;),但是缓存完全不需要增加的复杂性
Mark Kaplun 2016年

那是完全没有意义的。您要RAM缓存,Redis进行RAM缓存,周期;它做的很棒。如果您不想这样做,绝对不会增加任何复杂性。所以,先生,我真的不明白你的意思。
Cranio '16

0

选项始终存储在数据库中,而瞬变仅在安装了APC和在WP中实现APC缓存的插件时才可能存储在共享内存中。Memcache也使用内存。

选项也存储在内存中,并在可能的情况下从那里加载(如果没有,则执行db查询)。


0

好问题。

我认为WordPress如何使用的部分 WP_Object_Cache类的部分仍然缺失,因此我将其添加。

从文档:

DEF:WordPress对象缓存用于保存对数据库的旅行。对象缓存将所有缓存数据存储到内存中,并通过使用密钥来使缓存内容可用,该密钥用于命名和稍后检索缓存内容。

这是WP_Object_Cache结构。

在此处输入图片说明

注意+是公开的,-是私有的,#受保护。

您可以使用该stats()方法显示有关全局缓存对象及其中内容的常规统计信息。这是输出:

Cache Hits: 110
Cache Misses: 98

Group: options - ( 81.03k )
Group: default - ( 0.03k )
Group: users - ( 0.41k )
Group: userlogins - ( 0.03k )
Group: useremail - ( 0.04k )
Group: userslugs - ( 0.03k )
Group: user_meta - ( 3.92k )
Group: posts - ( 1.99k )
Group: terms - ( 1.76k )
Group: post_tag_relationships - ( 0.04k )
Group: category_relationships - ( 0.03k )
Group: post_format_relationships - ( 0.02k )
Group: post_meta - ( 0.36k )

这是我在模板开始时所获得的(例如)single.php

注意我们感兴趣的变量是: global $wp_object_cache

私有成员$cache拥有实际的缓存数据。

在编程中,缓存结构无处不在。以简单的形式,它们可以被识别为键值对。值区,NoDB结构,数据库索引。WordPress Object Cache的最终目标不是拥有最简单的结构,而是仍然可以识别键值对。

由于我是在single.php打印缓存时进入的:

print_r($wp_object_cache->cache['posts']);

我正在缓存一个帖子。

    [last_changed] => 0.34169600 1481802075
    [get_page_by_path:2516f01e446b6c125493ec7824b63868:0.34169600 1481802075] => 0
    [2831] => WP_Post Object
        (
            [ID] => 2831
            [post_author] => 1 
            ... the cached post object goes here
        )

该对象将是值,而缓存键将是

get_page_by_path:2516f01e446b6c125493ec7824b63868:0.34169600 1481802075

在这里,您可以检查$cache_key结构:

File: /wp-includes/post.php
4210: /**
4211:  * Retrieves a page given its path.
4212:  *
4213:  * @since 2.1.0
4214:  *
4215:  * @global wpdb $wpdb WordPress database abstraction object.
4216:  *
4217:  * @param string       $page_path Page path.
4218:  * @param string       $output    Optional. The required return type. One of OBJECT, ARRAY_A, or ARRAY_N, which correspond to
4219:  *                                a WP_Post object, an associative array, or a numeric array, respectively. Default OBJECT.
4220:  * @param string|array $post_type Optional. Post type or array of post types. Default 'page'.
4221:  * @return WP_Post|array|null WP_Post (or array) on success, or null on failure.
4222:  */
4223: function get_page_by_path( $page_path, $output = OBJECT, $post_type = 'page' ) {
4224:   global $wpdb;
4225: 
4226:   $last_changed = wp_cache_get_last_changed( 'posts' );
4227: 
4228:   $hash = md5( $page_path . serialize( $post_type ) );
4229:   $cache_key = "get_page_by_path:$hash:$last_changed";
4230:   $cached = wp_cache_get( $cache_key, 'posts' );
4231:   if ( false !== $cached ) {
4232:       // Special case: '0' is a bad `$page_path`.
4233:       if ( '0' === $cached || 0 === $cached ) {
4234:           return;
4235:       } else {
4236:           return get_post( $cached, $output );
4237:       }
4238:   }
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.