Comment fonctionne la mise en cache d'objets?

21

Je cherche une réponse définitive ici. Lorsque la mise en cache d'objets est activée, où se trouvent les options et les transitoires?

Par défaut, les deux sont stockés dans la base de données. Mais j'ai entendu des références selon lesquelles memcache les stockerait ailleurs et APC ferait autre chose entièrement. Où, exactement , ces données seront-elles conservées dans les deux cas?

EAMann
la source
2
L'article @toscho mentionne est maintenant disponible sur archive.org: Exploration de l'API WordPress Cache
ici

Réponses:

34

WordPress, par défaut, fait une forme de "mise en cache d'objet" mais sa durée de vie n'est qu'une charge de page unique.

Les options en sont un très bon exemple. Consultez cette réponse pour plus d'informations. Le résumé:

  1. Une page démarre
  2. Toutes les options sont chargées avec une simple SELECT option_name, option_value from $wpdb->optionsdéclaration
  3. Demandes ultérieures pour ces options (par exemple, un appel pour get_optionne jamais toucher la base de données car elles sont stockées avec l'API de cache WP.

Les options sont toujours "actives" dans la base de données et y sont toujours persistantes - c'est leur source "canonique". Cela dit, les options sont chargées dans le cache d'objets, donc lorsque vous demandez une option, il y a 99% de chances que la demande n'atteigne jamais la base de données.

Les transitoires sont un peu différents.

WordPress vous permet de remplacer l'API de cache par un drop-in - un fichier qui est placé directement dans votre wp-contentdossier. Si vous créez votre propre dépôt de cache ou utilisez un plugin existant , vous pouvez faire en sorte que le cache d'objets persiste plus longtemps qu'un chargement de page unique. Lorsque vous faites cela, transitoires, changez un peu.

Jetons un coup d'œil à la set_transientfonction dans 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 c'est vrai, WordPress utilise le cache d'objets au lieu de la base de données pour stocker les transitoires. Alors, comment cela devient-il vrai? Il est temps d'explorer comment WP configure sa propre API de cache.

Vous pouvez tracer presque tout vers wp-load.phpou wp-settings.php- qui sont tous deux cruciaux pour le processus d'amorçage de WordPress. Dans notre cache, il y a quelques lignes pertinentes wp-settings.php.

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

Rappelez-vous cette chute d'en haut? Jetons un coup d' oeil à 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' ) );
    }
}

Les lignes pertinentes de la fonction (celles qui s'y rapportent $_wp_using_ext_object_cachemodifient la façon dont les transitoires sont stockés).

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

s'il object-cache.phpexiste dans votre répertoire de contenu, il est inclus et WP suppose que vous utilisez un cache persistant externe - il est défini $_wp_using_ext_object_cachesur true.

Si vous utilisez un cache d'objets externe, les transitoires l'utiliseront. Ce qui soulève la question de savoir quand utiliser les options par rapport aux transitoires.

Simple. Si vous avez besoin que les données persistent indéfiniment, utilisez les options. Ils sont "mis en cache", mais leurs sources canoniques sont la base de données et ils ne disparaîtront jamais à moins qu'un utilisateur ne le demande explicitement.

Pour les données qui doivent être stockées pendant une durée définie, mais qui n'ont pas besoin de persister au-delà d'une durée de vie spécifiée, utiliser des transitoires. En interne, WP essaiera d'utiliser un cache d'objets persistant externe s'il le peut, sinon les données iront dans le tableau des options et récupèreront les ordures via psuedo-cron de WordPress à leur expiration.

Quelques autres préoccupations / questions:

  1. Est-il acceptable de faire une tonne d'appels à get_option? Probablement. Ils encourent l'appel à une surcharge de fonction, mais il n'atteindra probablement pas la base de données. La charge de la base de données est souvent une préoccupation plus importante dans l'évolutivité des applications Web que le travail que la langue de votre choix génère pour une page.
  2. Comment savoir comment utiliser les transitoires par rapport à l'API de cache? Si vous vous attendez à ce que les données persistent pendant une période définie, utilisez l'API transitoire. Si cela n'a pas d'importance si les données persistent (par exemple, il ne faut pas longtemps pour calculer / récupérer les données, mais cela ne devrait pas se produire plus d'une fois par chargement de page), utilisez l'API de cache.
  3. Toutes les options sont-elles vraiment mises en cache sur chaque chargement de page? Pas nécessairement. Si vous appelez add_optionavec son dernier argument facultatif car noils ne sont pas chargés automatiquement. Cela dit, une fois que vous les avez récupérés une fois, ils vont dans le cache et les appels suivants n'atteindront pas la base de données.
chrisguitarguy
la source
nitpick 1: toutes les options ne sont pas chargées au début de la page, mais seulement celles qui sont marquées "autoload = yes" lors de leur création. La valeur par défaut de ce paramètre dans add_option est 'yes' et la plupart des rédacteurs de plugins ne prennent pas la peine de comprendre la différence en utilisant un 'no', ce qui rend votre déclaration pratiquement vraie.
Mark Kaplun
Même les options non chargées automatiquement sont mises en cache après avoir été récupérées une fois. Ils peuvent ne pas être chargés initialement, mais ils vont ensuite dans le cache d'objets. Même les options qui n'existent pas sont mises en cache! github.com/WordPress/WordPress/blob/master/wp-includes/… J'ai cependant ajouté une note sur l'option de chargement automatique.
chrisguitarguy
c'était nitpick 2;)
Mark Kaplun
Merci pour cet excellent article et pour le temps qui résume tout cela.
prosti
5

Il y a 4 types de cache que je connais

  1. Trivial - Il est toujours activé et prend effet avant toute autre mise en cache. Il stocke les éléments mis en cache dans un tableau php, ce qui signifie qu'il consomme de la mémoire de votre session d'exécution php et que le cache est vidé une fois l'exécution php terminée. c'est-à-dire que même sans utiliser d'autre cache si vous appelez get_option ('opt') deux fois de suite, vous ne ferez une requête DB que la première fois et la deuxième fois la valeur sera retournée de la mémoire.

  2. Fichier - Les valeurs mises en cache sont stockées dans des fichiers quelque part sous votre répertoire racine. Je pense qu'il s'est avéré inefficace en termes de performances, sauf si vous disposez d'un stockage de fichiers mappé sur disque ou mémoire très rapide.

  3. APC (ou autre mise en cache basée sur un accélérateur php) - Les valeurs mises en cache sont stockées dans la mémoire de votre machine hôte et en dehors de votre allocation de mémoire php. Le plus grand écueil potentiel est qu'il n'y a pas de portée des données et que si vous exécutez deux sites, chacun peut potentiellement accéder aux données mises en cache de l'autre, ou les écraser.

  4. Memcache - c'est un cache basé sur le réseau. Vous pouvez exécuter le service de mise en cache n'importe où sur le réseau et il stocke probablement des valeurs dans sa mémoire hôte. Vous n'avez probablement pas besoin de memcache sauf si vous avez un équilibrage de charge en action.

BTW, la mise en cache d'objets met en cache bien plus que des options, elle stockera presque tout ce qui a été récupéré de la base de données à l'aide de l'API WP de haut niveau.

Mark Kaplun
la source
Je sais que la réponse est assez ancienne, mais j'ajouterais également l'excellent Redis .
Cranio
@Cranio, vous avez raison mais ... redis est fondamentalement une variante de memcache avec stockage, et c'est donc une base de données (NoSQL). Cette IMHO est en fait mauvaise car si le nœud échoue ou ne peut pas être mis à jour, vous pourriez en obtenir des informations périmées. Il a une option pour désactiver le comportement de type DB, mais je ne sais pas s'il est activé ou désactivé par défaut.
Mark Kaplun
C'est un remplacement parfait pour Memcached (encore mieux), de quoi d'autre avez-vous besoin? De loin, l'utilisation la plus courante que j'ai vue est simplement un stockage de valeur-clé RAM (oui, à part cela, les données peuvent être rendues persistantes, le clustering est en cours et a des capacités de gestion de file d'attente, mais tout le monde ajoute Redis comme un excellent option de mise en cache pour WP)
Cranio
tout le monde peut également sauter du pont;) mais la complexité supplémentaire n'est absolument pas nécessaire pour la mise en cache
Mark Kaplun
C'est complètement inutile; vous voulez la mise en cache RAM, Redis fait la mise en cache RAM, point; et il le fait à merveille. Il n'y a absolument aucune complexité supplémentaire si vous ne voulez pas y aller. Donc, monsieur, je ne comprends vraiment pas votre point.
Cranio
0

Les options sont toujours stockées dans la base de données, tandis que les transitoires peuvent être stockés uniquement dans la mémoire partagée si APC et un plugin qui implémente la mise en cache APC dans WP est installé. Memcache utilise également la mémoire.

Les options sont également stockées en mémoire et chargées à partir de là lorsque cela est possible (sinon, une requête db est effectuée).

onetrickpony
la source
0

Grande question.

Je pense que la partie sur la façon dont WordPress utilise la WP_Object_Cacheclasse est toujours manquante, alors j'ajouterai cela.

De la documentation:

DEF: Le cache d'objets WordPress est utilisé pour économiser sur les déplacements vers la base de données. Le cache d'objets stocke toutes les données du cache en mémoire et rend le contenu du cache disponible à l'aide d'une clé, qui est utilisée pour nommer et récupérer ultérieurement le contenu du cache.

Voici la WP_Object_Cachestructure.

entrez la description de l'image ici

Remarque + est public, - privé, # protégé.

Vous utilisez la stats()méthode pour afficher des statistiques générales sur l'objet de cache global et ce qu'il contient. Voici la sortie:

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 )

C'est ce que j'ai eu avant au tout début d'un modèle tel que single.php.

Notez que la variable qui nous intéresse est: global $wp_object_cache.

Privé, le membre $cachedétient les données de mise en cache réelles.

En programmation, les structures de cache sont partout. Sous une forme simple, ils peuvent être reconnus comme une paire de valeurs clés. Seaux, structures NoDB, index de base de données. L'objectif ultime de WordPress Object Cache n'était pas d'avoir la structure la plus simple possible, mais des paires de valeurs clés peuvent toujours être reconnues.

Depuis que j'étais dedans single.phpquand j'ai imprimé le cache:

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

Je reçois un seul post mis en cache.

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

L'objet serait la valeur et la clé de mise en cache serait

get_page_by_path:2516f01e446b6c125493ec7824b63868:0.34169600 1481802075

Ici, vous pouvez vérifier la $cache_keystructure:

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
la source