Jak działa buforowanie obiektów?

21

Szukam ostatecznej odpowiedzi tutaj. Kiedy buforowanie obiektów jest włączone, gdzie opcje i stany przejściowe kończą się?

Domyślnie oba są przechowywane w bazie danych. Ale słyszałem pewne odniesienia, że ​​memcache przechowa je gdzie indziej, a APC zrobi coś zupełnie innego. Gdzie, dokładnie te dane zostaną utrwalone w obu przypadkach?

EAMann
źródło
2
Artykuł, o którym wspomina @toscho, jest teraz dostępny na archive.org: Eksplorowanie interfejsu API pamięci podręcznej WordPress
tutaj

Odpowiedzi:

34

WordPress domyślnie ma formę „buforowania obiektów”, ale jego żywotność to tylko ładowanie jednej strony.

Opcje są naprawdę dobrym tego przykładem. Sprawdź tę odpowiedź, aby uzyskać więcej informacji. Podsumowanie:

  1. Strona się zaczyna
  2. Wszystkie opcje są ładowane za pomocą prostego SELECT option_name, option_value from $wpdb->options instrukcji
  3. Kolejne żądania tych opcji (np. Wezwanie do get_optionnigdy nie trafienia do bazy danych, ponieważ są one przechowywane z API pamięci podręcznej WP).

Opcje zawsze znajdują się „na żywo” w bazie danych i zawsze są tam przechowywane - to jest ich „kanoniczne” źródło. To powiedziawszy, opcje są ładowane do pamięci podręcznej obiektów, więc kiedy poprosisz o opcję, istnieje 99% szans, że żądanie nigdy nie trafi do bazy danych.

Stany przejściowe są nieco inne.

WordPress pozwala zastąpić interfejs pamięci podręcznej drop-in - plik, który jest umieszczany bezpośrednio w wp-contentfolderze. Jeśli utworzysz własną pamięć podręczną, upuść lub użyj istniejącej wtyczki , możesz sprawić, że pamięć podręczna obiektów będzie utrzymywać się dłużej niż ładowanie pojedynczej strony. Kiedy to zrobisz, stany przejściowe, trochę się zmień.

Rzućmy okiem na set_transientfunkcję w 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? Jeśli to prawda, WordPress zamiast tego używa pamięci podręcznej obiektów bazy danych do przechowywania stanów nieustalonych. Jak to się dzieje do prawdy? Czas zbadać, w jaki sposób WP konfiguruje własny interfejs API pamięci podręcznej.

Możesz prześledzić prawie wszystko do wp-load.phplub wp-settings.php- oba są kluczowe dla procesu ładowania WordPressa. W naszej pamięci podręcznej znajduje się kilka odpowiednich wierszy wp-settings.php.

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

Pamiętasz ten spadek z góry? Rzućmy okiem na wp_start_object_cachew 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' ) );
    }
}

Odpowiednie linie funkcji (odnoszące się do $_wp_using_ext_object_cachetego zmieniają sposób przechowywania stanów nieustalonych).

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

jeśli object-cache.phpistnieje w twoim katalogu zawartości, zostaje dołączony i WP zakłada, że ​​używasz zewnętrznej, trwałej pamięci podręcznej - ustawia się $_wp_using_ext_object_cachena true.

Jeśli korzystasz z zewnętrznego obiektu pamięci podręcznej, będą go używać transjenty. Co rodzi pytanie o to, kiedy należy używać opcji vs. stanów nieustalonych.

Prosty. Jeśli potrzebujesz danych, aby trwały bez końca, skorzystaj z opcji. Dostają „buforowane”, ale ich kanonicznymi źródłami jest baza danych i nigdy nie odejdą, chyba że użytkownik wyraźnie o to poprosi.

W przypadku danych, które powinny być przechowywane przez określony czas, ale nie muszą trwać dłużej niż określone przejściowe okresy użytkowania. Wewnętrznie WP spróbuje użyć zewnętrznej, trwałej pamięci podręcznej obiektów, jeśli będzie to możliwe, w przeciwnym razie dane przejdą do tabeli opcji i zostaną usunięte śmieci za pomocą psuedo-cron WordPressa po ich wygaśnięciu.

Niektóre inne obawy / pytania:

  1. Czy można wykonywać mnóstwo połączeń z get_option? Prawdopodobnie. Powodują wywołanie funkcji narzutu funkcji, ale prawdopodobnie nie trafi ona do bazy danych. Ładowanie bazy danych jest często większym problemem w zakresie skalowalności aplikacji internetowych niż praca generowana przez wybrany język.
  2. Skąd mam wiedzieć, jak używać transjentów w porównaniu z API Cache? Jeśli oczekujesz, że dane będą się utrzymywać przez określony czas, użyj przejściowego interfejsu API. Jeśli nie ma znaczenia, czy dane będą się utrzymywać (np. Obliczenie / pobranie danych nie potrwa długo, ale nie powinno się to zdarzyć częściej niż raz na ładowanie strony), użyj interfejsu API pamięci podręcznej.
  3. Czy wszystkie opcje są naprawdę buforowane przy każdym ładowaniu strony? Niekoniecznie. Jeśli zadzwonisz add_optionz ostatnim, opcjonalnym argumentem, ponieważ nonie są one automatycznie ładowane. To powiedziawszy, gdy raz je ściągniesz, przejdą do pamięci podręcznej, a kolejne połączenia nie trafią do bazy danych.
chrisguitarguy
źródło
nitpick 1: Nie wszystkie opcje są ładowane na początku strony, ale tylko te, które są oznaczone jako „autoload = yes” podczas tworzenia. Domyślnym ustawieniem tego parametru w opcji add_option jest „tak”, a większość autorów wtyczek nie zadaje sobie trudu, aby zrozumieć różnicę w stosowaniu „nie”, dzięki czemu twoje stwierdzenie jest praktycznie prawdziwe.
Mark Kaplun,
Nawet opcje nieautomatycznie ładowane są buforowane po ich pobraniu raz. Nie mogą być początkowo ładowane, ale potem przechodzą do pamięci podręcznej obiektów. Nawet opcje, które nie istnieją, są buforowane! github.com/WordPress/WordPress/blob/master/wp-include/ ... Dodałem jednak notatkę o opcji automatycznego ładowania.
chrisguitarguy
to był nitpick 2;)
Mark Kaplun
Dziękuję za świetny artykuł i za podsumowanie tego wszystkiego.
prosti
5

Istnieją 4 typy pamięci podręcznej, o których wiem

  1. Trywialny - jest zawsze włączony i działa, zanim w grę wejdzie inne buforowanie. Przechowuje buforowane elementy w tablicy php, co oznacza, że ​​zużywa pamięć z sesji wykonawczej php i że pamięć podręczna jest opróżniana po zakończeniu wykonywania php. tzn. nawet bez użycia innej pamięci podręcznej, jeśli wywołasz get_option ('opt') dwa razy z rzędu, wykonasz zapytanie DB tylko za pierwszym razem, a za drugim razem wartość zostanie zwrócona z pamięci.

  2. Plik - buforowane wartości są przechowywane w plikach gdzieś w katalogu głównym. Uważam, że okazało się to nieskuteczne pod względem wydajności, chyba że masz bardzo szybki dysk lub pamięć plików zmapowanych w pamięci.

  3. APC (lub inne buforowanie oparte na akceleratorze php) - Wartości buforowane są przechowywane w pamięci komputera hosta i poza alokacją pamięci php. Największą potencjalną pułapką jest to, że nie ma zasięgu danych, a jeśli prowadzisz dwie witryny, potencjalnie każda z nich może uzyskać dostęp do danych w pamięci podręcznej drugiej lub je zastąpić.

  4. Pamięć podręczna - jest to pamięć podręczna oparta na sieci. Możesz uruchomić usługę buforowania w dowolnym miejscu w sieci i prawdopodobnie przechowuje ona wartości w pamięci hosta. Prawdopodobnie nie potrzebujesz memcache, chyba że masz równoważenie obciążenia w akcji.

BTW, buforowanie obiektów jest buforowaniem znacznie więcej niż opcji, będzie przechowywać prawie wszystko, co zostało pobrane z bazy danych przy użyciu WP API na wysokim poziomie.

Mark Kaplun
źródło
Wiem, że odpowiedź jest dość stara, ale dodałbym również doskonałą Redis .
Cranio
@Cranio, masz rację, ale ... redis jest w zasadzie odmianą memcache z pamięcią, a zatem jest to DB (NoSQL). To IMHO jest złe, ponieważ jeśli węzeł ulegnie awarii lub nie będzie można go zaktualizować, możesz uzyskać z niego nieaktualne informacje. Ma opcję wyłączenia zachowania podobnego do DB, ale nie jestem pewien, czy jest on domyślnie włączony czy wyłączony.
Mark Kaplun,
Jest to idealny zamiennik Memcached (jeszcze lepiej), czego jeszcze potrzebujesz? Zdecydowanie najczęstszym zastosowaniem, które widziałem, jest po prostu pamięć RAM-wartość (tak, poza tym dane mogą być trwałe, klastrowanie jest w drodze i ma możliwości zarządzania kolejkami, ale wszyscy dodają Redis jako doskonałą opcja buforowania dla WP)
Cranio
każdy może również skoczyć z mostu;), ale dodatkowa złożoność nie jest potrzebna do buforowania
Mark Kaplun
To całkowicie bezcelowe; chcesz buforować pamięć RAM, Redis wykonuje buforowanie pamięci RAM, kropka; i robi to cudownie. Nie ma absolutnie żadnej złożoności, jeśli nie chcesz tego robić. Tak więc, proszę pana, naprawdę nie rozumiem waszej tezy.
Cranio
0

Opcje są zawsze przechowywane w bazie danych, a transjenty mogą być przechowywane tylko we wspólnej pamięci, jeśli zainstalowano APC i wtyczkę, która implementuje buforowanie APC w WP. Memcache również wykorzystuje pamięć.

Opcje są również przechowywane w pamięci i ładowane stamtąd, jeśli to możliwe (jeśli nie, wykonywane jest zapytanie db).

onetrickpony
źródło
0

Świetne pytanie.

Myślę, że WP_Object_Cachewciąż brakuje części tego, w jaki sposób WordPress używa klasy, więc dodam to.

Z dokumentów:

DEF: Pamięć podręczna obiektów WordPress służy do zapisywania podróży do bazy danych. Pamięć podręczna obiektów przechowuje wszystkie dane pamięci podręcznej w pamięci i udostępnia zawartość pamięci podręcznej za pomocą klucza, który służy do nazywania i późniejszego pobierania zawartości pamięci podręcznej.

Oto WP_Object_Cachestruktura.

wprowadź opis zdjęcia tutaj

Note + jest publiczny, - prywatny, # chroniony.

stats()Metoda ta służy do wyświetlania ogólnych statystyk dotyczących globalnego obiektu pamięci podręcznej i tego, co tam jest. Oto wynik:

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 )

To właśnie otrzymałem na samym początku szablonu, takiego jak single.php.

Uwaga zmienna jesteśmy zainteresowani jest: global $wp_object_cache.

Prywatne członek $cacheprzechowuje rzeczywiste dane buforowania.

W programowaniu struktury pamięci podręcznej są wszędzie. W prostej formie można je rozpoznać jako parę klucz-wartość. Wiadra, struktury NoDB, indeksy baz danych. Ostatecznym celem WordPress Object Cache nie była najprostsza możliwa struktura, ale nadal można rozpoznać pary kluczowych wartości.

Odkąd byłem, single.phpkiedy wydrukowałem pamięć podręczną:

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

Dostaję buforowany pojedynczy post.

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

Obiekt byłby wartością, a kluczem buforującym byłby

get_page_by_path:2516f01e446b6c125493ec7824b63868:0.34169600 1481802075

Tutaj możesz sprawdzić $cache_keystrukturę:

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
źródło