¿Cómo funciona el almacenamiento en caché de objetos?

21

Estoy buscando una respuesta definitiva aquí. Cuando el almacenamiento en caché de objetos está habilitado, ¿dónde acaban viviendo las opciones y los transitorios?

Por defecto, ambos se almacenan en la base de datos. Pero he escuchado algunas referencias de que Memcache los almacenará en otro lugar y APC hará algo completamente diferente. ¿Dónde, exactamente , se conservarán estos datos en ambos casos?

EAMann
fuente
2
El artículo que menciona @toscho ahora está disponible en archive.org: Explorando la API de caché de WordPress
aquí

Respuestas:

34

WordPress, de forma predeterminada, realiza una forma de "almacenamiento en caché de objetos", pero su vida útil es solo una carga de página.

Las opciones son realmente un buen ejemplo de esto. Mira esta respuesta para más información. El resumen:

  1. Comienza una página
  2. Todas las opciones se cargan con una SELECT option_name, option_value from $wpdb->optionsdeclaración simple
  3. Solicitudes posteriores para esas opciones (por ejemplo, una llamada para que get_optionnunca llegue a la base de datos porque están almacenadas con la API de caché de WP.

Las opciones siempre "viven" en la base de datos y siempre se mantienen allí, esa es su fuente "canónica". Dicho esto, las opciones se cargan en el caché de objetos, por lo que cuando solicita una opción hay un 99% de posibilidades de que la solicitud nunca llegue a la base de datos.

Los transitorios son un poco diferentes.

WordPress le permite reemplazar la API de caché con un menú desplegable , un archivo que se coloca directamente en su wp-contentcarpeta. Si crea su propia memoria caché o usa un complemento existente , puede hacer que la memoria caché del objeto persista más de una sola carga de página. Cuando haces eso, transitorios, cambian un poco.

Echemos un vistazo a la set_transientfunción en 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;
}

Hmmm $_wp_using_ext_object_cache? Si es cierto, WordPress usa el caché de objetos en lugar de la base de datos para almacenar transitorios. Entonces, ¿cómo se establece eso en verdad? Es hora de explorar cómo WP configura su propia API de caché.

Puede rastrear casi todo hasta wp-load.phpo wp-settings.php, los cuales son cruciales para el proceso de arranque de WordPress. En nuestro caché, hay algunas líneas relevantes en wp-settings.php.

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

¿Recuerdas esa caída desde arriba? Vamos a echar un vistazo a wp_start_object_cacheen wp-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' ) );
    }
}

Las líneas relevantes de la función (las que pertenecen a $_wp_using_ext_object_cacheeso alteran la forma en que se almacenan los transitorios).

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;
}

si object-cache.phpexiste en su directorio de contenido, se incluye y WP asume que está utilizando un caché externo y persistente; se establece $_wp_using_ext_object_cacheen verdadero.

Si está utilizando un objeto externo, los transeúntes lo usarán. Lo que plantea la cuestión de cuándo usar las opciones frente a los transitorios.

Simple. Si necesita datos para persistir indefinidamente, use las opciones. Se "almacenan en caché", pero sus fuentes canónicas son la base de datos y nunca desaparecerán a menos que un usuario lo solicite explícitamente.

Para los datos que deben almacenarse durante un período de tiempo establecido, pero que no necesitan persistir más allá de una vida útil específica, utilice transitorios. Internamente, WP intentará usar un caché de objetos externo y persistente si puede, de lo contrario, los datos irán a la tabla de opciones y se recolectará la basura a través de psuedo-cron de WordPress cuando caduquen.

Algunas otras preocupaciones / preguntas:

  1. ¿Está bien hacer un montón de llamadas get_option? Probablemente. Incurren en la llamada a una sobrecarga de la función, pero es probable que no llegue a la base de datos. La carga de la base de datos suele ser una preocupación mayor en la escalabilidad de las aplicaciones web que el trabajo que el idioma de su elección genera al generar una página.
  2. ¿Cómo sé usar transitorios frente a la API de caché? Si espera que los datos persistan durante un período determinado, use la API transitoria. Si no importa si los datos persisten (por ejemplo, no toma mucho tiempo calcular / recuperar los datos, pero no debería suceder más de una vez por carga de página) use la API de caché.
  3. ¿Todas las opciones están realmente en caché en cada carga de página? No necesariamente. Si llama add_optioncon su último argumento opcional, ya noque no se cargan automáticamente. Dicho esto, una vez que los recuperas una vez, van al caché y las llamadas posteriores no afectarán la base de datos.
chrisguitarguy
fuente
nitpick 1: no todas las opciones se cargan al inicio de la página, pero solo las que están marcadas como "autoload = yes" cuando se crean. El valor predeterminado para ese parámetro en add_option es 'sí' y la mayoría de los escritores de complementos no se molestan en comprender la diferencia al usar un 'no' para que su declaración sea prácticamente verdadera.
Mark Kaplun
Incluso las opciones no cargadas automáticamente se almacenan en caché después de recuperarlas una vez. Puede que no se carguen inicialmente, pero luego van a la caché de objetos. ¡Incluso las opciones que no existen están en caché! github.com/WordPress/WordPress/blob/master/wp-includes/… Sin embargo, agregué una nota sobre la opción de carga automática.
chrisguitarguy
that was nitpick 2;)
Mark Kaplun
Gracias por el gran artículo, y por el tiempo que resume todo eso.
prosti
5

Hay 4 tipos de caché que conozco

  1. Trivial: siempre está activado y surte efecto antes de que cualquier otro almacenamiento en caché entre en juego. Almacena los elementos almacenados en caché en una matriz php, lo que significa que consume memoria de su sesión de ejecución de php, y que el caché se vacía después de que finaliza la ejecución de php. es decir, incluso sin usar ningún otro caché si llama a get_option ('opt') dos veces seguidas, realizará una consulta de base de datos solo la primera vez y la segunda vez que el valor se devolverá de la memoria.

  2. Archivo: los valores almacenados en caché se almacenan en archivos en algún lugar de su directorio raíz. Creo que resultó no ser efectivo en términos de rendimiento a menos que tenga un disco muy rápido o un almacenamiento de archivos mapeados en memoria.

  3. APC (u otro almacenamiento en caché basado en el acelerador php): los valores almacenados en caché se almacenan en la memoria de su máquina host y fuera de su asignación de memoria php. La mayor dificultad potencial es que no hay alcance de datos y si ejecuta dos sitios, potencialmente cada uno puede acceder a los datos almacenados en caché del otro o sobrescribirlos.

  4. Memcache: es un caché basado en la red. Puede ejecutar el servicio de almacenamiento en caché en cualquier lugar de la red y probablemente almacena valores en su memoria host. Probablemente no necesite memcache a menos que tenga un equilibrio de carga en acción.

Por cierto, el almacenamiento en caché de objetos es mucho más que las opciones, almacenará casi todo lo que se recuperó de la base de datos utilizando la API WP de alto nivel.

Mark Kaplun
fuente
Sé que la respuesta es bastante antigua, pero agregaría también el excelente Redis .
Cranio
@ Craneo, tienes razón, pero ... redis es básicamente una variante de memcache con almacenamiento, y por lo tanto es un DB (NoSQL). Este IMHO es realmente malo, ya que si el nodo falla o no se puede actualizar, es posible que obtenga información obsoleta. Tiene una opción para desactivar el comportamiento similar a la base de datos, pero no estoy seguro de si está activado o desactivado de forma predeterminada.
Mark Kaplun
Es un reemplazo perfecto para Memcached (aún mejor), ¿qué más necesitas? Con mucho, el uso más común que he visto es solo como un almacenamiento de valor de clave RAM (sí, aparte de eso, los datos pueden hacerse persistentes, la agrupación está en camino y tiene capacidades de gestión de colas, pero todos agregan Redis como un excelente opción de almacenamiento en caché para WP)
Cranio
todos también pueden saltar del puente;) pero la complejidad adicional no es necesaria para el almacenamiento en caché
Mark Kaplun
Eso es completamente inútil; desea el almacenamiento en caché de RAM, Redis hace el almacenamiento en caché de RAM, punto; y lo hace maravillosamente No hay absolutamente ninguna complejidad añadida si no quieres hacerlo. Entonces, señor, realmente no entiendo su punto.
Cranio
0

Las opciones siempre se almacenan en la base de datos, mientras que los transitorios se pueden almacenar solo en la memoria compartida si se instala APC y un complemento que implementa el almacenamiento en caché de APC en WP. Memcache también usa memoria.

Las opciones también se almacenan en la memoria y se cargan desde allí cuando es posible (si no, se realiza una consulta de base de datos).

Pony de un solo truco
fuente
0

Gran pregunta

Creo que WP_Object_Cachetodavía falta la parte de cómo WordPress usa la clase, por lo que agregaré eso.

De los documentos:

DEF: La caché de objetos de WordPress se usa para guardar en viajes a la base de datos. Object Cache almacena todos los datos de la memoria caché en la memoria y hace que el contenido de la memoria caché esté disponible mediante el uso de una clave, que se usa para nombrar y luego recuperar el contenido de la memoria caché.

Aquí está la WP_Object_Cacheestructura.

ingrese la descripción de la imagen aquí

Note + es público, - privado, # protegido.

Utiliza el stats()método para mostrar estadísticas generales sobre el objeto de caché global y lo que hay allí. Aquí está la salida:

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 )

Esto es lo que obtuve antes al comienzo de una plantilla como single.php.

Tenga en cuenta la variable que nos interesa es: global $wp_object_cache.

El privado del miembro $cachecontiene los datos de almacenamiento en caché reales.

En programación, las estructuras de caché están en todas partes. De forma simple, se pueden reconocer como un par de valores clave. Cubos, estructuras NoDB, índices de bases de datos. El objetivo final de WordPress Object Cache no era tener la estructura más simple posible, pero aún así se pueden reconocer los pares de valores clave.

Como estaba single.phpcuando imprimí el caché:

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

Estoy recibiendo una sola publicación en caché.

    [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
        )

El objeto sería el valor, y la clave de almacenamiento en caché sería

get_page_by_path:2516f01e446b6c125493ec7824b63868:0.34169600 1481802075

Aquí puede verificar la $cache_keyestructura:

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:   }
prosti
fuente