Cum funcționează stocarea în cache a obiectelor?

2 dec. 2012, 23:23:26
Vizualizări: 21.4K
Voturi: 24

Caut un răspuns definitiv aici. Când este activată stocarea în cache a obiectelor, unde ajung să fie stocate opțiunile și tranzientele?

În mod implicit, ambele sunt stocate în baza de date. Dar am auzit unele referințe că Memcache le va stoca în altă parte și APC va face ceva complet diferit. Unde vor fi persistate aceste date, exact, în ambele cazuri?

1
Comentarii

Articolul menționat de @toscho este acum disponibil pe archive.org: Explorând API-ul de Cache WordPress

here here
11 nov. 2015 02:29:03
Toate răspunsurile la întrebare 4
6
37

WordPress, în mod implicit, efectuează o formă de "Cache de Obiecte", dar durata sa este limitată la încărcarea unei singure pagini.

Opțiunile sunt un exemplu foarte bun în acest sens. Consultați acest răspuns pentru mai multe informații. Rezumatul:

  1. Se încarcă o pagină
  2. Toate opțiunile sunt încărcate printr-o simplă instrucțiune SELECT option_name, option_value from $wpdb->options
  3. Cererile ulterioare pentru acele opțiuni (de exemplu, un apel la get_option) nu mai accesează baza de date, deoarece sunt stocate prin intermediul API-ului de cache WordPress.

Opțiunile există întotdeauna în baza de date și sunt persistente acolo — aceasta este sursa lor "canonică". Cu toate acestea, opțiunile sunt încărcate în cache-ul de obiecte, astfel încât atunci când solicitați o opțiune, există o șansă de 99% ca acea solicitare să nu mai acceseze baza de date.

Tranzientele sunt puțin diferite.

WordPress vă permite să înlocuiți API-ul de cache cu un drop-in — un fișier plasat direct în folderul wp-content. Dacă creați propriul fișier de cache drop-in sau folosiți un plugin existent plugin, puteți face ca cache-ul de obiecte să persiste mai mult decât o singură încărcare de pagină. Când faceți acest lucru, tranzientele se comportă puțin diferit.

Să analizăm funcția set_transient din wp-includes/option.php.

<?php
/**
 * Setează/actualizează valoarea unei tranziente.
 *
 * Nu este necesar să serializați valorile. Dacă valoarea trebuie serializată, 
 * aceasta va fi serializată înainte de a fi setată.
 *
 * @since 2.8.0
 * @package WordPress
 * @subpackage Transient
 *
 * @uses apply_filters() Apelurile 'pre_set_transient_$transient' permit suprascrierea
 *  valorii tranziente care urmează să fie stocată.
 * @uses do_action() Apelurile 'set_transient_$transient' și 'setted_transient' sunt executate la succes.
 *
 * @param string $transient Numele tranzientei. Nu trebuie să fie escapată pentru SQL.
 * @param mixed $value Valoarea tranzientei. Nu trebuie să fie escapată pentru SQL.
 * @param int $expiration Timpul până la expirare în secunde, implicit 0
 * @return bool False dacă valoarea nu a fost setată și true dacă valoarea a fost setată.
 */
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? Dacă este true, WordPress folosește cache-ul de obiecte în locul bazei de date pentru a stoca tranzientele. Deci, cum devine acesta true? Să explorăm cum WordPress configurează propriul API de cache.

Puteți urmări aproape totul până la wp-load.php sau wp-settings.php — ambele fiind cruciale pentru procesul de inițializare WordPress. În cazul nostru, există câteva linii relevante în wp-settings.php.

// Pornește cache-ul de obiecte WordPress sau un cache extern dacă există drop-in-ul.
wp_start_object_cache();

Vă amintiți de acel drop-in menționat mai devreme? Să analizăm funcția wp_start_object_cache din wp-includes/load.php.

<?php
/**
 * Pornește cache-ul de obiecte WordPress.
 *
 * Dacă există un fișier object-cache.php în directorul wp-content,
 * acesta este folosit ca cache extern de obiecte.
 *
 * @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' ) ) {
        // Uneori, advanced-cache.php poate încărca object-cache.php înainte să fie încărcat aici.
        // Acest lucru poate strica verificarea function_exists de mai sus și poate duce la setarea incorectă a $_wp_using_ext_object_cache.
        // Verifică dublu dacă există un cache extern.
        $_wp_using_ext_object_cache = true;
    }

    // Dacă cache-ul suportă resetarea, resetează în loc de inițializare dacă este deja inițializat.
    // Resetarea semnalează cache-ului că ID-urile globale s-au schimbat și poate fi nevoie să actualizeze cheile
    // și să curețe cache-urile.
    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' ) );
    }
}

Liniile relevante ale funcției (cele care influențează $_wp_using_ext_object_cache și modifică modul în care sunt stocate tranzientele).

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

Dacă fișierul object-cache.php există în directorul de conținut, acesta este inclus și WordPress presupune că utilizați un cache extern persistent — setează $_wp_using_ext_object_cache la true.

Dacă utilizați un cache extern de obiecte, tranzientele îl vor folosi. Ceea ce ridică întrebarea: când să folosiți opțiuni vs. tranziente?

Simplu. Dacă aveți nevoie ca datele să persiste indefinit, utilizați opțiuni. Acestea sunt "cachate", dar sursa lor canonică este baza de date și nu vor dispărea decât dacă un utilizator le șterge în mod explicit.

Pentru date care trebuie stocate pentru o perioadă determinată, dar nu trebuie să persiste după expirarea acesteia, utilizați tranziente. Intern, WordPress va încerca să folosească un cache extern persistent dacă este posibil; altfel, datele vor fi stocate în tabelul de opțiuni și vor fi curățate prin intermediul pseudo-cron-ului WordPress după expirare.

Alte considerații/întrebări:

  1. Este în regulă să faci multe apeluri la get_option? Probabil. Acestea implică un overhead de apelare a funcției, dar este puțin probabil să acceseze baza de date. Încărcarea bazei de date este adesea o problemă mai mare în scalabilitatea aplicațiilor web decât munca depusă de limbajul de programare pentru a genera o pagină.
  2. Cum știu când să folosesc tranziente vs. API-ul de Cache? Dacă vă așteptați ca datele să persiste pentru o perioadă determinată, utilizați API-ul de tranziente. Dacă nu contează dacă datele persistă (de exemplu, nu durează mult să le calculați/obțineți, dar nu ar trebui să se întâmple mai mult de o dată pe încărcare de pagină), utilizați API-ul de cache.
  3. Toate opțiunile sunt într-adevăr încărcate în cache la fiecare încărcare de pagină? Nu neapărat. Dacă apelați add_option cu ultimul parametru opțional setat la no, acestea nu sunt încărcate automat. Totuși, odată ce sunt preluate o dată, sunt introduse în cache, iar apelurile ulterioare nu vor accesa baza de date.
3 dec. 2012 07:19:47
Comentarii

observație minoră 1: Nu toate opțiunile sunt încărcate la pornirea paginii, ci doar cele care sunt marcate cu "autoload=da" la creare. Valoarea implicită pentru acest parametru în add_option este 'da' și majoritatea dezvoltatorilor de plugin-uri nu se obosesc să înțeleagă diferența de a folosi 'nu' acolo, ceea ce face afirmația ta practic adevărată.

Mark Kaplun Mark Kaplun
3 dec. 2012 07:32:29

Chiar și opțiunile care nu se încarcă automat sunt memorate în cache după ce sunt preluate o dată. Este posibil să nu fie încărcate inițial, dar intră în cache-ul obiect după aceea. Chiar și opțiunile care nu există sunt memorate în cache! https://github.com/WordPress/WordPress/blob/master/wp-includes/option.php#L58-L71 Am adăugat totuși o notă despre opțiunea de autoload.

chrisguitarguy chrisguitarguy
3 dec. 2012 07:37:21

asta a fost observația minoră 2 ;)

Mark Kaplun Mark Kaplun
3 dec. 2012 07:42:31

Mulțumesc pentru articolul excelent și pentru timpul acordat pentru a rezuma toate acestea.

prosti prosti
15 dec. 2016 15:54:31

Ce răspuns minunat și util, mulțumesc!

And Finally And Finally
11 iun. 2020 11:42:04

@chrisguitarguy: Ce se întâmplă când modifici $_wp_using_ext_object_cache în timpul execuției, adică prin utilizarea wp_using_ext_object_cache(false)? Aceasta dezactivează temporar pentru această încărcare de pagină cache-ul pentru apelurile ulterioare la baza de date/cache?

tim tim
5 ian. 2022 19:15:14
Arată celelalte 1 comentarii
7

Există 4 tipuri de cache pe care le cunosc

  1. Trivial - Este mereu activ și intră în acțiune înaintea oricărui alt tip de caching. Stochează elementele cache într-un array PHP, ceea ce înseamnă că consumă memorie din sesiunea de execuție PHP, iar cache-ul este golit după ce execuția PHP se termină. Adică, chiar fără a folosi alt cache, dacă apelezi get_option('opt') de două ori consecutiv, vei face o interogare la baza de date doar prima dată, iar a doua oară valoarea va fi returnată din memorie.

  2. Fișiere - Valorile cache sunt stocate în fișiere undeva în directorul rădăcină. Cred că s-a dovedit a fi ineficient din punct de vedere al performanței, cu excepția cazului în ai un disc foarte rapid sau stocare prin fișiere mapate în memorie.

  3. APC (sau alt caching bazat pe accelerator PHP) - Valorile cache sunt stocate în memoria mașinii gazdă, în afara alocării de memorie PHP. Cea mai mare problemă potențială este că nu există delimitare a datelor, iar dacă rulezi două site-uri, fiecare poate accesa sau suprascrie datele cache ale celuilalt.

  4. Memcache - este un cache bazat pe rețea. Poți rula serviciul de caching oriunde în rețea și probabil stochează valori în memoria gazdei. Probabil nu ai nevoie de Memcache decât dacă ai un sistem de load balancing în acțiune.

Apropo, object caching înseamnă mult mai mult decât caching pentru opțiuni, va stoca aproape orice a fost preluat din baza de date folosind API-ul de nivel înalt al WordPress.

3 dec. 2012 07:13:52
Comentarii

Știu că răspunsul este destul de vechi, dar aș adăuga și excelentul Redis.

Cranio Cranio
12 apr. 2016 13:32:27

@Cranio, ai dreptate dar... redis este practic o variantă de memcache cu stocare și, prin urmare, este o bază de date (NoSQL). Aceasta IMHO este de fapt un lucru rău, deoarece dacă nodul eșuează sau nu poate fi actualizat, s-ar putea să obții informații învechite de la el. Are o opțiune pentru a dezactiva comportamentul de bază de date, dar nu sunt sigur dacă este activată sau dezactivată implicit.

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

Este un înlocuitor perfect pentru Memcached (chiar mai bun), ce altceva ai nevoie? De departe, cel mai frecvent utilizare pe care am văzut-o este doar ca o stocare cheie-valoare în RAM (da, în afară de asta, datele pot fi făcute persistente, clustering-ul este în curs de implementare și are capabilități de gestionare a cozilor, dar toată lumea adaugă Redis ca o excelentă opțiune de caching pentru WP)

Cranio Cranio
12 apr. 2016 15:45:58

toți pot să sară și de pe pod ;) dar complexitatea adăugată nu este deloc necesară pentru caching

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

Este complet inutil; vrei caching în RAM, Redis face caching în RAM, punct; și o face minunat. Nu există absolut nicio complexitate adăugată dacă nu vrei să mergi pe această cale. Așadar, domnule, chiar nu înțeleg punctul tău de vedere.

Cranio Cranio
13 apr. 2016 13:41:08

LOL dacă ai nevoie de 5 linii de cod pentru a face X, și eu îți ofer cele 5 linii plus încă 10, prin definiție are o complexitate mai mare. Nu există niciun motiv să schimbi tehnologiile doar din cauza hype-ului. Dacă poți demonstra că Redis este mai bun în orice fel legat de object caching, atunci poate ai un argument, dar nu este cazul.

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

Din nou, acest lucru este complet lipsit de sens în încercarea de a NU include Redis în listă. Presupun că din motive strict personale, care contravin cu totul scopului StackOverflow. Nu este o bătălie copilărească despre ce este mai bun, ci despre prezentarea opțiunilor și tehnologiilor disponibile. Pentru restul: http://stackoverflow.com/questions/10558465/memcached-vs-redis

Cranio Cranio
13 apr. 2016 13:56:13
Arată celelalte 2 comentarii
0

Bună întrebare.

Cred că partea cu modul în care WordPress folosește clasa WP_Object_Cache încă lipsește, așa că voi adăuga asta.

Din documentație:

DEF: Cache-ul de Obiecte WordPress este folosit pentru a reduce numărul de accesări la baza de date. Cache-ul de Obiecte stochează toate datele de cache în memorie și pune conținutul cache-ului la dispoziție folosind o cheie, care este utilizată pentru a denumi și apoi pentru a recupera conținutul cache-ului.

Iată structura WP_Object_Cache.

descriere imagine

Notă: + este public, - privat, # protejat.

Folosești metoda stats() pentru a afișa statistici generale despre obiectul global de cache și despre ce conține. Iată rezultatul:

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 )

Acesta este rezultatul pe care l-am obținut chiar la începutul unui șablon precum single.php.

Variabila care ne interesează este: global $wp_object_cache.

Membrul privat $cache stochează datele efective de cache.

În programare, structurile de cache sunt peste tot. În forma cea mai simplă, ele pot fi recunoscute ca perechi cheie-valoare. Găleți, structuri NoDB, indexuri de bază de date. Scopul final al Cache-ului de Obiecte WordPress nu a fost să aibă cea mai simplă structură posibilă, dar totuși perechile cheie-valoare pot fi recunoscute.

Deoarece mă aflam în single.php când am afișat cache-ul:

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

Am obținut un singur post stocat în cache.

    [last_changed] => 0.34169600 1481802075
    [get_page_by_path:2516f01e446b6c125493ec7824b63868:0.34169600 1481802075] => 0
    [2831] => WP_Post Object
        (
            [ID] => 2831
            [post_author] => 1 
            ... aici continuă obiectul postului stocat în cache
        )

Obiectul ar fi valoarea, iar cheia de cache ar fi

get_page_by_path:2516f01e446b6c125493ec7824b63868:0.34169600 1481802075

Aici puteți verifica structura $cache_key:

Fișier: /wp-includes/post.php
4210: /**
4211:  * Returnează o pagină dată fiind calea acesteia.
4212:  *
4213:  * @since 2.1.0
4214:  *
4215:  * @global wpdb $wpdb Obiectul de abstractizare a bazei de date WordPress.
4216:  *
4217:  * @param string       $page_path Calea paginii.
4218:  * @param string       $output    Opțional. Tipul de return cerut. Unul dintre OBJECT, ARRAY_A sau ARRAY_N, care corespund
4219:  *                                unui obiect WP_Post, unui array asociativ sau unui array numeric, respectiv. Implicit OBJECT.
4220:  * @param string|array $post_type Opțional. Tipul de post sau array de tipuri de posturi. Implicit 'page'.
4221:  * @return WP_Post|array|null WP_Post (sau array) în caz de succes, sau null în caz de eșec.
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:       // Caz special: '0' este o `$page_path` invalidă.
4233:       if ( '0' === $cached || 0 === $cached ) {
4234:           return;
4235:       } else {
4236:           return get_post( $cached, $output );
4237:       }
4238:   }
15 dec. 2016 15:53:34
0

Opțiunile sunt întotdeauna stocate în baza de date, în timp ce transient-urile pot fi stocate doar în memoria partajată dacă APC și un plugin care implementează caching APC în WP este instalat. Memcache folosește de asemenea memoria.

Opțiunile sunt de asemenea stocate în memorie și încărcate de acolo atunci când este posibil (dacă nu, se execută o interogare în baza de date).

3 dec. 2012 06:54:53