Come funziona il caching degli oggetti?

2 dic 2012, 23:23:26
Visualizzazioni: 21.4K
Voti: 24

Sto cercando una risposta definitiva qui. Quando il caching degli oggetti è abilitato, dove finiscono per essere memorizzati le options e i transients?

Per impostazione predefinita, entrambi vengono memorizzati nel database. Ma ho sentito alcuni riferimenti secondo cui Memcache li memorizza altrove e APC fa qualcosa di completamente diverso. Dove vengono persistiti questi dati esattamente in entrambi i casi?

1
Commenti

L'articolo menzionato da @toscho è ora disponibile su archive.org: Exploring the WordPress Cache API

here here
11 nov 2015 02:29:03
Tutte le risposte alla domanda 4
6
37

WordPress, per impostazione predefinita, implementa una forma di "Object Caching" ma la sua durata è limitata al caricamento di una singola pagina.

Le opzioni sono un ottimo esempio di questo comportamento. Dai un'occhiata a questa risposta per maggiori informazioni. Ecco un riepilogo:

  1. Inizia il caricamento di una pagina
  2. Tutte le opzioni vengono caricate con una semplice query SELECT option_name, option_value from $wpdb->options
  3. Le richieste successive per quelle opzioni (ad esempio una chiamata a get_option) non interrogano il database perché sono memorizzate tramite l'API di cache di WP.

Le opzioni "vivono" sempre nel database e vi sono sempre memorizzate in modo permanente — questa è la loro fonte "canonica". Detto ciò, le opzioni vengono caricate nella cache degli oggetti, quindi quando richiedi un'opzione c'è il 99% di probabilità che tale richiesta non interroghi mai il database.

I transient sono un po' diversi.

WordPress ti consente di sostituire l'API della cache con un drop-in — un file che viene posizionato direttamente nella tua cartella wp-content. Se crei il tuo drop-in per la cache o usi un plugin esistente come questo, puoi far sì che la cache degli oggetti persista oltre il caricamento di una singola pagina. Quando lo fai, il comportamento dei transient cambia leggermente.

Diamo un'occhiata alla funzione set_transient in wp-includes/option.php.

<?php
/**
 * Imposta/aggiorna il valore di un transient.
 *
 * Non è necessario serializzare i valori. Se il valore deve essere serializzato, verrà
 * serializzato automaticamente prima di essere impostato.
 *
 * @since 2.8.0
 * @package WordPress
 * @subpackage Transient
 *
 * @uses apply_filters() Chiama l'hook 'pre_set_transient_$transient' per consentire la sovrascrittura
 *  del valore del transient da memorizzare.
 * @uses do_action() Chiama gli hook 'set_transient_$transient' e 'setted_transient' in caso di successo.
 *
 * @param string $transient Nome del transient. Non deve essere escapato per SQL.
 * @param mixed $value Valore del transient. Non deve essere escapato per SQL.
 * @param int $expiration Tempo fino alla scadenza in secondi, default 0
 * @return bool False se il valore non è stato impostato, true se è stato impostato.
 */
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? Se è true, WordPress utilizza la cache degli oggetti invece del database per memorizzare i transient. Ma come viene impostato su true? È ora di esplorare come WP configura la sua API di cache.

Puoi risalire quasi tutto a wp-load.php o wp-settings.php — entrambi cruciali per il processo di bootstrap di WordPress. Nel nostro caso, ci sono alcune righe rilevanti in wp-settings.php.

// Avvia la cache degli oggetti di WordPress, o una cache esterna se è presente il drop-in.
wp_start_object_cache();

Ricordi quel drop-in di cui parlavamo prima? Diamo un'occhiata a wp_start_object_cache in wp-includes/load.php.

<?php
/**
 * Avvia la cache degli oggetti di WordPress.
 *
 * Se esiste un file object-cache.php nella directory wp-content,
 * lo utilizza come cache esterna degli oggetti.
 *
 * @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' ) ) {
        // A volte advanced-cache.php può caricare object-cache.php prima che venga caricato qui.
        // Ciò può invalidare il controllo function_exists sopra e portare a un'impostazione errata di $_wp_using_ext_object_cache.
        // Verifica nuovamente se esiste una cache esterna.
        $_wp_using_ext_object_cache = true;
    }

    // Se la cache supporta il reset, resetta invece di inizializzare se già inizializzata.
    // Il reset segnala alla cache che gli ID globali sono cambiati e potrebbe essere necessario aggiornare le chiavi
    // e pulire le cache.
    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' ) );
    }
}

Le righe rilevanti della funzione (quelle relative a $_wp_using_ext_object_cache che alterano il modo in cui i transient vengono memorizzati).

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

Se object-cache.php esiste nella tua directory dei contenuti, viene incluso e WP assume che stai utilizzando una cache esterna e persistente — imposta $_wp_using_ext_object_cache su true.

Se stai utilizzando una cache esterna degli oggetti, i transient la utilizzeranno. Il che solleva la questione di quando usare le opzioni rispetto ai transient.

Semplice. Se hai bisogno che i dati persistano indefinitamente, usa le opzioni. Vengono "memorizzate nella cache", ma la loro fonte canonica è il database e non scompariranno mai a meno che un utente non lo richieda esplicitamente.

Per i dati che devono essere memorizzati per un periodo di tempo determinato, ma non devono persistere oltre una durata specificata, usa i transient. Internamente, WP cercherà di utilizzare una cache esterna e persistente degli oggetti, se possibile; altrimenti i dati finiranno nella tabella delle opzioni e verranno raccolti come garbage tramite il pseudo-cron di WordPress quando scadranno.

Alcune altre considerazioni/domande:

  1. Va bene fare molte chiamate a get_option? Probabilmente sì. Hanno il sovraccarico della chiamata a una funzione, ma è improbabile che interroghino il database. Il carico sul database è spesso una preoccupazione maggiore nella scalabilità delle applicazioni web rispetto al lavoro che il tuo linguaggio di programmazione fa per generare una pagina.
  2. Come faccio a sapere se usare i transient o l'API della Cache? Se ti aspetti che i dati persistano per un periodo determinato, usa l'API dei transient. Se non importa che i dati persistano (ad esempio, non ci vuole molto tempo per calcolare/recuperare i dati, ma non dovrebbe accadere più di una volta per caricamento della pagina), usa l'API della cache.
  3. Vengono davvero memorizzate tutte le opzioni in cache ad ogni caricamento di pagina? Non necessariamente. Se chiami add_option con il suo ultimo argomento opzionale impostato su no, non vengono caricate automaticamente. Detto ciò, una volta che le recuperi una volta, vanno nella cache e le chiamate successive non interrogano il database.
3 dic 2012 07:19:47
Commenti

pignoleria 1: Non tutte le opzioni vengono caricate all'avvio della pagina, ma solo quelle contrassegnate come "autoload=yes" al momento della creazione. Il valore predefinito per quel parametro in add_option è 'yes' e la maggior parte degli sviluppatori di plugin non si preoccupa di capire la differenza nell'usare un 'no' lì, rendendo la tua affermazione praticamente vera.

Mark Kaplun Mark Kaplun
3 dic 2012 07:32:29

Anche le opzioni non autocaricate vengono memorizzate nella cache dopo essere state recuperate una volta. Potrebbero non essere caricate inizialmente, ma finiscono nell'object cache successivamente. Persino le opzioni che non esistono vengono memorizzate nella cache! https://github.com/WordPress/WordPress/blob/master/wp-includes/option.php#L58-L71 Ho aggiunto una nota riguardo all'opzione autoload, comunque.

chrisguitarguy chrisguitarguy
3 dic 2012 07:37:21

quella era la pignoleria 2 ;)

Mark Kaplun Mark Kaplun
3 dic 2012 07:42:31

Grazie per il fantastico articolo e per il tempo dedicato a riassumere tutto.

prosti prosti
15 dic 2016 15:54:31

Che risposta brillante e utile, grazie!

And Finally And Finally
11 giu 2020 11:42:04

@chrisguitarguy: Cosa succede quando si modifica $_wp_using_ext_object_cache durante il runtime, ad esempio utilizzando wp_using_ext_object_cache(false)? Questo disabilita temporaneamente per questo caricamento di pagina la cache per le successive chiamate al DB/Cache?

tim tim
5 gen 2022 19:15:14
Mostra i restanti 1 commenti
7

Conosco 4 tipi di cache

  1. Banale - È sempre attiva e ha effetto prima che entri in gioco qualsiasi altro tipo di caching. Memorizza gli elementi nella cache in un array PHP, il che significa che consuma memoria dalla sessione di esecuzione PHP e che la cache viene svuotata al termine dell'esecuzione PHP. Ad esempio, anche senza utilizzare altre cache, se chiami get_option('opt') due volte di seguito, effettuerai una query al database solo la prima volta e la seconda il valore verrà restituito dalla memoria.

  2. File - I valori memorizzati nella cache vengono salvati in file da qualche parte sotto la tua directory root. Credo si sia dimostrato non efficace in termini di prestazioni a meno che non si disponga di un disco molto veloce o di un archivio file mappato in memoria.

  3. APC (o altre cache basate su acceleratori PHP) - I valori memorizzati nella cache vengono salvati nella memoria del computer host e al di fuori dell'allocazione di memoria PHP. La potenziale insidia più grande è che non c'è delimitazione dei dati e se esegui due siti, potenzialmente ognuno può accedere ai dati in cache dell'altro o sovrascriverli.

  4. Memcache - È una cache basata su rete. Puoi eseguire il servizio di caching ovunque sulla rete e probabilmente memorizza i valori nella memoria del suo host. Probabilmente non hai bisogno di Memcache a meno che non sia in azione un bilanciamento del carico.

BTW, l'object caching memorizza nella cache molto più che le opzioni, archivia quasi tutto ciò che è stato recuperato dal database utilizzando l'API di alto livello di WordPress.

3 dic 2012 07:13:52
Commenti

Sono consapevole che la risposta sia piuttosto vecchia, ma vorrei aggiungere anche l'ottimo Redis.

Cranio Cranio
12 apr 2016 13:32:27

@Cranio, hai ragione ma... Redis è fondamentalmente una variante di Memcache con storage, e quindi è un database (NoSQL). Questo secondo me è un aspetto negativo perché se il nodo fallisce o non può essere aggiornato, potresti ottenere informazioni obsolete da esso. Ha un'opzione per disattivare il comportamento da database, ma non sono sicuro se sia attiva o disattivata di default.

Mark Kaplun Mark Kaplun
12 apr 2016 13:42:19

È un sostituto perfetto per Memcached (addirittura migliore), cos'altro ti serve? Di gran lunga l'utilizzo più comune che ho visto è semplicemente come storage chiave-valore in RAM (sì, oltre a questo, i dati possono essere resi persistenti, il clustering è in arrivo e ha capacità di gestione delle code, ma tutti aggiungono Redis come un'ottima opzione di caching per WP)

Cranio Cranio
12 apr 2016 15:45:58

tutti possono anche saltare dal ponte ;) ma la complessità aggiuntiva non è assolutamente necessaria per il caching

Mark Kaplun Mark Kaplun
12 apr 2016 16:04:28

Questo è completamente inutile; vuoi il caching in RAM, Redis fa caching in RAM, punto; e lo fa meravigliosamente. Non c'è assolutamente nessuna complessità aggiuntiva se non vuoi approfondire. Quindi, signore, davvero non riesco a capire il suo punto.

Cranio Cranio
13 apr 2016 13:41:08

LOL se hai bisogno di 5 righe di codice per fare X, e io ti offro le 5 righe più altre 10, per definizione ha una complessità maggiore. Non c'è motivo di cambiare tecnologia solo per moda. Se puoi dimostrare che Redis è migliore in qualche modo legato all'object caching, allora forse avrai un punto, ma non lo è.

Mark Kaplun Mark Kaplun
13 apr 2016 13:53:11

Ancora una volta, è completamente inutile nello sforzo di NON includere Redis tra la lista. Presumo, per ragioni strettamente personali che vanno completamente contro lo scopo di StackOverflow. Non è una guerra infantile su cosa sia meglio, si tratta di mostrare le opzioni e le tecnologie disponibili. Per il resto: http://stackoverflow.com/questions/10558465/memcached-vs-redis

Cranio Cranio
13 apr 2016 13:56:13
Mostra i restanti 2 commenti
0

Ottima domanda.

Penso che manchi ancora la parte su come WordPress utilizza la classe WP_Object_Cache, quindi la aggiungerò.

Dalla documentazione:

DEF: La WordPress Object Cache viene utilizzata per ridurre i viaggi al database. L'Object Cache memorizza tutti i dati della cache in memoria e rende i contenuti della cache disponibili utilizzando una chiave, che viene utilizzata per nominare e successivamente recuperare i contenuti della cache.

Ecco la struttura di WP_Object_Cache.

descrizione dell'immagine

Nota: + è pubblico, - privato, # protetto.

Si utilizza il metodo stats() per mostrare statistiche generali sull'oggetto cache globale e su cosa contiene. Ecco l'output:

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 )

Questo è quello che ho ottenuto all'inizio di un template come single.php.

Nota la variabile che ci interessa è: global $wp_object_cache.

Il membro privato $cache contiene i dati effettivi della cache.

Nella programmazione, le strutture di cache sono ovunque. In una forma semplice possono essere riconosciute come coppie chiave-valore. Bucket, strutture NoDB, indici del database. L'obiettivo finale della WordPress Object Cache non era avere la struttura più semplice possibile, ma si possono comunque riconoscere le coppie chiave-valore.

Poiché ero in single.php quando ho stampato la cache:

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

Ottengo un singolo post memorizzato nella cache.

    [last_changed] => 0.34169600 1481802075
    [get_page_by_path:2516f01e446b6c125493ec7824b63868:0.34169600 1481802075] => 0
    [2831] => WP_Post Object
        (
            [ID] => 2831
            [post_author] => 1 
            ... qui continua l'oggetto post memorizzato nella cache
        )

L'oggetto sarebbe il valore, e la chiave della cache sarebbe

get_page_by_path:2516f01e446b6c125493ec7824b63868:0.34169600 1481802075

Qui puoi controllare la struttura di $cache_key:

File: /wp-includes/post.php
4210: /**
4211:  * Recupera una pagina dato il suo percorso.
4212:  *
4213:  * @since 2.1.0
4214:  *
4215:  * @global wpdb $wpdb Oggetto di astrazione del database WordPress.
4216:  *
4217:  * @param string       $page_path Percorso della pagina.
4218:  * @param string       $output    Opzionale. Il tipo di ritorno richiesto. Uno tra OBJECT, ARRAY_A o ARRAY_N, che corrispondono
4219:  *                                rispettivamente a un oggetto WP_Post, un array associativo o un array numerico. Predefinito OBJECT.
4220:  * @param string|array $post_type Opzionale. Tipo di post o array di tipi di post. Predefinito 'page'.
4221:  * @return WP_Post|array|null WP_Post (o array) in caso di successo, o null in caso di fallimento.
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:       // Caso speciale: '0' è un `$page_path` non valido.
4233:       if ( '0' === $cached || 0 === $cached ) {
4234:           return;
4235:       } else {
4236:           return get_post( $cached, $output );
4237:       }
4238:   }
15 dic 2016 15:53:34
0

Le opzioni sono sempre memorizzate nel database, mentre i transient possono essere memorizzati solo nella memoria condivisa se APC e un plugin che implementa la memorizzazione APC in WP è installato. Memcache utilizza anch'esso la memoria.

Le opzioni sono inoltre memorizzate in memoria e caricate da lì quando possibile (in caso contrario, viene eseguita una query al database).

3 dic 2012 06:54:53