大家好,今天咱们聊聊WordPress里面一个挺重要,但又容易被忽视的东西:object cache。别看名字挺高大上,其实就是个缓存,但它对WordPress的性能影响可大了。咱们重点分析wp-includes/cache.php
这个文件,看看WordPress是怎么设计这个缓存接口的。
开场白:缓存的重要性,以及Object Cache在WordPress中的地位
先问大家一个问题:你们有没有遇到过打开WordPress网站,感觉像蜗牛爬一样慢的情况?大部分情况下,这跟数据库查询脱不了干系。每次用户访问页面,WordPress都得吭哧吭哧地从数据库里捞数据,捞完再组装成网页。如果访问量一大,数据库就扛不住了。
这时候,缓存就派上用场了。缓存就像一个“快速通道”,把经常用到的数据存起来,下次再需要的时候,直接从缓存里取,不用再去数据库里折腾。
而Object Cache,就是WordPress用来缓存各种对象(比如文章、用户、选项等)的机制。它可以显著减少数据库查询次数,提高网站的响应速度。
wp-includes/cache.php
:缓存接口的定义
wp-includes/cache.php
这个文件,并没有具体实现缓存的逻辑,它只是定义了一组缓存接口。换句话说,它规定了缓存应该有哪些基本操作,但具体怎么实现,由其他的缓存后端来负责。
这样做的好处是,WordPress可以很灵活地选择不同的缓存后端,比如Memcached、Redis、APCu等,而不用修改核心代码。
咱们先来看看这个文件里定义了哪些关键的类和函数:
-
WP_Object_Cache
类: 这个是所有缓存后端类的父类。它定义了一些抽象方法,比如add()
、get()
、set()
、delete()
等,这些方法是所有缓存后端都必须实现的。 -
wp_cache_add()
函数: 添加一个数据到缓存,只有当缓存中不存在相同键值时才添加。 -
wp_cache_get()
函数: 从缓存中获取数据。 -
wp_cache_set()
函数: 设置一个数据到缓存。 -
wp_cache_delete()
函数: 从缓存中删除数据。 -
wp_cache_replace()
函数: 替换缓存中已存在的数据。 -
wp_cache_flush()
函数: 清空整个缓存。 -
wp_cache_close()
函数: 关闭缓存连接(如果需要)。
WP_Object_Cache
类的剖析
WP_Object_Cache
类是所有缓存后端的基础。咱们看看它的主要成员变量和方法:
class WP_Object_Cache {
/**
* Holds the cached objects.
*
* @var array
*/
public $cache = array();
/**
* The number of cache calls.
*
* @var int
*/
public $cache_hits = 0;
/**
* Number of times cache missed.
*
* @var int
*/
public $cache_misses = 0;
/**
* The amount of times the cache was reset.
*
* @var int
*/
public $reset_count = 0;
/**
* List of global groups.
*
* @var array
*/
public $global_groups = array();
/**
* The blog ID.
*
* @var int
*/
public $blog_id = 0;
/**
* Holds the value if WordPress is installed.
*
* @var bool
*/
public $multisite;
/**
* Holds the save queries.
*
* @var array
*/
public $queries = array();
/**
* Whether to save queries.
*
* @var bool
*/
public $save_queries = false;
/**
* Sets up object cache global.
*
* @since 2.0.0
* @access public
*
* @global int $blog_id
*/
public function __construct() {
global $blog_id;
$this->multisite = is_multisite();
if ( defined( 'CUSTOM_USER_TABLE' ) ) {
$this->global_groups[] = 'users';
}
if ( defined( 'CUSTOM_USER_META_TABLE' ) ) {
$this->global_groups[] = 'user_meta';
}
$this->blog_id = (int) $blog_id;
}
/**
* Adds a key and value to the cache.
*
* This function is meant to be overloaded in a subclass.
*
* @since 2.0.0
* @access public
* @abstract
*
* @param int|string $key The key under which to store the value.
* @param mixed $data The data to store.
* @param string $group Optional. The group name to isolate the cache entry.
* Default 'default'.
* @param int $expire Optional. When the cache data should expire, in seconds.
* Default 0 (no expiration).
* @return bool False if the key and value were not set, true on success.
*/
public function add( $key, $data, $group = 'default', $expire = 0 ) {
return false;
}
/**
* Retrieves the cache contents, if it exists.
*
* The function is meant to be overloaded in a subclass. If no value is
* found, then false is returned.
*
* @since 2.0.0
* @access public
* @abstract
*
* @param int|string $key The key under which to store the value.
* @param string $group Optional. The group name to isolate the cache entry.
* Default 'default'.
* @param bool $force Optional. Whether to force a refresh of the local
* cache from the persistent cache. Default false.
* @param bool $found Optional. Whether the key was found in the cache.
* Pass by reference to a variable that will be updated
* accordingly. Default null.
* @return mixed|false The cache contents on success, false if the value was not found
* in the cache.
*/
public function get( $key, $group = 'default', $force = false, &$found = null ) {
return false;
}
/**
* Sets the data contents into the cache.
*
* The function is meant to be overloaded in a subclass.
*
* @since 2.0.0
* @access public
* @abstract
*
* @param int|string $key The key under which to store the value.
* @param mixed $data The data to store.
* @param string $group Optional. The group name to isolate the cache entry.
* Default 'default'.
* @param int $expire Optional. When the cache data should expire, in seconds.
* Default 0 (no expiration).
* @return bool True on success, false on failure.
*/
public function set( $key, $data, $group = 'default', $expire = 0 ) {
return false;
}
/**
* Removes the key (and value) from the cache.
*
* The function is meant to be overloaded in a subclass.
*
* @since 2.0.0
* @access public
* @abstract
*
* @param int|string $key The key to delete from the cache.
* @param string $group Optional. The group name to isolate the cache entry.
* Default 'default'.
* @return bool True on success, false on failure.
*/
public function delete( $key, $group = 'default' ) {
return false;
}
/**
* Replaces the contents of the cache, if it exists.
*
* The function is meant to be overloaded in a subclass.
*
* @since 2.0.0
* @access public
* @abstract
*
* @param int|string $key The key under which to store the value.
* @param mixed $data The data to store.
* @param string $group Optional. The group name to isolate the cache entry.
* Default 'default'.
* @param int $expire Optional. When the cache data should expire, in seconds.
* Default 0 (no expiration).
* @return bool True if the value was replaced, false if not.
*/
public function replace( $key, $data, $group = 'default', $expire = 0 ) {
return false;
}
/**
* Clears the object cache of all data.
*
* The function is meant to be overloaded in a subclass.
*
* @since 2.0.0
* @access public
* @abstract
*
* @return bool False on failure, true on success.
*/
public function flush() {
return false;
}
/**
* Closes the cache.
*
* This function is meant to be overloaded in a subclass.
*
* @since 2.0.0
* @access public
*
* @return bool True on success, false on failure.
*/
public function close() {
return true;
}
/**
* Resets internal state.
*
* Resets the internal state of the cache. Useful for unit tests.
*
* @since 3.5.0
* @access public
*/
public function reset() {
$this->cache = array();
$this->cache_hits = 0;
$this->cache_misses = 0;
$this->reset_count++;
}
/**
* Adds a group or set of groups to the list of global groups.
*
* @since 2.6.0
* @access public
*
* @param string|array $groups A group or an array of groups to add.
*/
public function add_global_groups( $groups ) {
if ( ! is_array( $groups ) ) {
$groups = array( $groups );
}
$this->global_groups = array_merge( $this->global_groups, $groups );
$this->global_groups = array_unique( $this->global_groups );
}
/**
* Switch the blog id.
*
* This changes the blog ID used by the cache. Invalidate the cache keys
* after switching blogs.
*
* @since 3.5.0
* @access public
*
* @param int $blog_id Blog ID.
*/
public function switch_to_blog( $blog_id ) {
$blog_id = (int) $blog_id;
$this->blog_id = $blog_id;
$this->reset();
}
/**
* Get the blog id.
*
* @since 4.5.0
* @access public
*
* @return int The blog id.
*/
public function get_blog_id() {
return $this->blog_id;
}
/**
* Increments numeric cache item's value.
*
* @since 3.3.0
* @access public
*
* @param int|string $key The key for the cache entry to increment.
* @param int $offset Optional. The amount to increment by. Default 1.
* @param string $group Optional. The group to place the cache entry in.
* Default 'default'.
* @return int|false The item's new value on success, false on failure.
*/
public function incr( $key, $offset = 1, $group = 'default' ) {
return false;
}
/**
* Decrements numeric cache item's value.
*
* @since 3.3.0
* @access public
*
* @param int|string $key The key for the cache entry to decrement.
* @param int $offset Optional. The amount to decrement by. Default 1.
* @param string $group Optional. The group to place the cache entry in.
* Default 'default'.
* @return int|false The item's new value on success, false on failure.
*/
public function decr( $key, $offset = 1, $group = 'default' ) {
return false;
}
/**
* Adds multiple key-value pairs to the cache.
*
* @since 5.5.0
* @access public
*
* @param array $data An array of key-value pairs to add. Each key is the cache key, and the value is the data to be cached.
* @param string $group Optional. The group name to isolate the cache entries.
* Default 'default'.
* @param int $expire Optional. When the cache data should expire, in seconds.
* Default 0 (no expiration).
* @return bool[] An array of booleans, one for each key in `$data`.
* Each value indicates whether the key and value were successfully set.
*/
public function add_multiple( array $data, $group = 'default', $expire = 0 ) {
$results = array();
foreach ( $data as $key => $value ) {
$results[ $key ] = $this->add( $key, $value, $group, $expire );
}
return $results;
}
/**
* Replaces multiple key-value pairs in the cache.
*
* @since 5.5.0
* @access public
*
* @param array $data An array of key-value pairs to replace. Each key is the cache key, and the value is the data to be cached.
* @param string $group Optional. The group name to isolate the cache entries.
* Default 'default'.
* @param int $expire Optional. When the cache data should expire, in seconds.
* Default 0 (no expiration).
* @return bool[] An array of booleans, one for each key in `$data`.
* Each value indicates whether the key and value were successfully replaced.
*/
public function replace_multiple( array $data, $group = 'default', $expire = 0 ) {
$results = array();
foreach ( $data as $key => $value ) {
$results[ $key ] = $this->replace( $key, $value, $group, $expire );
}
return $results;
}
/**
* Sets multiple key-value pairs in the cache.
*
* @since 5.5.0
* @access public
*
* @param array $data An array of key-value pairs to set. Each key is the cache key, and the value is the data to be cached.
* @param string $group Optional. The group name to isolate the cache entries.
* Default 'default'.
* @param int $expire Optional. When the cache data should expire, in seconds.
* Default 0 (no expiration).
* @return bool[] An array of booleans, one for each key in `$data`.
* Each value indicates whether the key and value were successfully set.
*/
public function set_multiple( array $data, $group = 'default', $expire = 0 ) {
$results = array();
foreach ( $data as $key => $value ) {
$results[ $key ] = $this->set( $key, $value, $group, $expire );
}
return $results;
}
/**
* Gets multiple values from the cache in one call.
*
* @since 5.5.0
* @access public
*
* @param array $keys An array of keys to retrieve from the cache.
* @param string $group Optional. The group name to isolate the cache entries.
* Default 'default'.
* @param bool $force Optional. Whether to force a refresh of the local
* cache from the persistent cache. Default false.
* @return array An array of key-value pairs. Each key is the cache key, and the value is the data that was cached.
* If a key does not exist in the cache, its value will be `false`.
*/
public function get_multiple( array $keys, $group = 'default', $force = false ) {
$results = array();
foreach ( $keys as $key ) {
$results[ $key ] = $this->get( $key, $group, $force );
}
return $results;
}
/**
* Deletes multiple values from the cache in one call.
*
* @since 5.5.0
* @access public
*
* @param array $keys An array of keys to be deleted from the cache.
* @param string $group Optional. The group name to isolate the cache entries.
* Default 'default'.
* @return bool[] An array of booleans, one for each key in `$keys`.
* Each value indicates whether the key was successfully deleted.
*/
public function delete_multiple( array $keys, $group = 'default' ) {
$results = array();
foreach ( $keys as $key ) {
$results[ $key ] = $this->delete( $key, $group );
}
return $results;
}
}
$cache
: 一个数组,用于存储缓存的数据。注意,这个数组只是在内存中缓存数据,如果进程重启或者服务器重启,数据就没了。所以,WP_Object_Cache
本身只是一个简单的内存缓存。$cache_hits
和$cache_misses
: 记录缓存命中和未命中的次数,可以用来评估缓存的效率。$global_groups
: 存储全局组的名称。全局组的数据在所有博客(如果是多站点)之间共享。$blog_id
: 当前博客的 ID。在多站点环境下,可以使用switch_to_blog()
方法切换博客,并重置缓存。add()
、get()
、set()
、delete()
、replace()
、flush()
、close()
: 这些都是抽象方法,需要在子类中实现具体的缓存逻辑。
缓存操作函数 (wp_cache_add()
、wp_cache_get()
等)
wp-includes/cache.php
文件还定义了一些全局函数,用于方便地进行缓存操作。这些函数实际上是对 WP_Object_Cache
实例的方法的封装。
/**
* Adds data to the cache, if the cache key doesn't already exist.
*
* @since 2.0.0
*
* @see WP_Object_Cache::add()
*
* @param int|string $key The cache key to use for retrieval later.
* @param mixed $data The data to add to the cache.
* @param string $group Optional. The group to add the cache to. Allows for ungrouped cache values.
* Default empty string.
* @param int $expire Optional. When the cache data should expire, in seconds.
* Default 0 (no expiration).
* @return bool False if not stored, true on success.
*/
function wp_cache_add( $key, $data, $group = '', $expire = 0 ) {
global $wp_object_cache;
return $wp_object_cache->add( $key, $data, $group, $expire );
}
/**
* Retrieves data from the cache.
*
* @since 2.0.0
*
* @see WP_Object_Cache::get()
*
* @param int|string $key The key to retrieve the value for.
* @param string $group Optional. The group the key is in. Allows for ungrouped cache values.
* Default empty string.
* @param bool $force Optional. Whether to force a refresh of the local
* cache from the persistent cache. Default false.
* @param bool $found Optional. Whether the key was found in the cache.
* Pass by reference to a variable that will be updated
* accordingly. Default null.
* @return mixed|false The cache contents on success, false if the value was not found
* in the cache.
*/
function wp_cache_get( $key, $group = '', $force = false, &$found = null ) {
global $wp_object_cache;
return $wp_object_cache->get( $key, $group, $force, $found );
}
/**
* Saves data to the cache.
*
* @since 2.0.0
*
* @see WP_Object_Cache::set()
*
* @param int|string $key The cache key to use for retrieval later.
* @param mixed $data The data to store in the cache.
* @param string $group Optional. The group to add the cache to. Allows for ungrouped cache values.
* Default empty string.
* @param int $expire Optional. When the cache data should expire, in seconds.
* Default 0 (no expiration).
* @return bool True on success, false on failure.
*/
function wp_cache_set( $key, $data, $group = '', $expire = 0 ) {
global $wp_object_cache;
return $wp_object_cache->set( $key, $data, $group, $expire );
}
/**
* Removes data from the cache.
*
* @since 2.0.0
*
* @see WP_Object_Cache::delete()
*
* @param int|string $key The cache key to delete.
* @param string $group Optional. The group to delete the cache from. Allows for ungrouped cache values.
* Default empty string.
* @return bool True on success, false on failure.
*/
function wp_cache_delete( $key, $group = '' ) {
global $wp_object_cache;
return $wp_object_cache->delete( $key, $group );
}
/**
* Replaces the contents of the cache, if it exists.
*
* @since 3.0.0
*
* @see WP_Object_Cache::replace()
*
* @param int|string $key The cache key to use for retrieval later.
* @param mixed $data The data to store in the cache.
* @param string $group Optional. The group to add the cache to. Allows for ungrouped cache values.
* Default empty string.
* @param int $expire Optional. When the cache data should expire, in seconds.
* Default 0 (no expiration).
* @return bool True if contents were replaced, false if not.
*/
function wp_cache_replace( $key, $data, $group = '', $expire = 0 ) {
global $wp_object_cache;
return $wp_object_cache->replace( $key, $data, $group, $expire );
}
/**
* Clears the object cache of all data.
*
* @since 2.0.0
*
* @see WP_Object_Cache::flush()
*
* @return bool Always true.
*/
function wp_cache_flush() {
global $wp_object_cache;
return $wp_object_cache->flush();
}
/**
* Closes the cache.
*
* This function is meant to be overloaded in a subclass.
*
* @since 2.0.0
*
* @see WP_Object_Cache::close()
*
* @return bool True on success, false on failure.
*/
function wp_cache_close() {
global $wp_object_cache;
return $wp_object_cache->close();
}
/**
* Increments numeric cache item's value
*
* @since 3.3.0
*
* @see WP_Object_Cache::incr()
*
* @param int|string $key The key for the cache entry to increment.
* @param int $offset Optional. The amount to increment by. Default 1.
* @param string $group Optional. The group to place the cache entry in.
* Default 'default'.
* @return int|false The item's new value on success, false on failure.
*/
function wp_cache_incr( $key, $offset = 1, $group = 'default' ) {
global $wp_object_cache;
return $wp_object_cache->incr( $key, $offset, $group );
}
/**
* Decrements numeric cache item's value
*
* @since 3.3.0
*
* @see WP_Object_Cache::decr()
*
* @param int|string $key The key for the cache entry to decrement.
* @param int $offset Optional. The amount to decrement by. Default 1.
* @param string $group Optional. The group to place the cache entry in.
* Default 'default'.
* @return int|false The item's new value on success, false on failure.
*/
function wp_cache_decr( $key, $offset = 1, $group = 'default' ) {
global $wp_object_cache;
return $wp_object_cache->decr( $key, $offset, $group );
}
/**
* Adds multiple key-value pairs to the cache.
*
* @since 5.5.0
*
* @see WP_Object_Cache::add_multiple()
*
* @param array $data An array of key-value pairs to add. Each key is the cache key, and the value is the data to be cached.
* @param string $group Optional. The group to add the cache to. Allows for ungrouped cache values.
* Default empty string.
* @param int $expire Optional. When the cache data should expire, in seconds.
* Default 0 (no expiration).
* @return bool[] An array of booleans, one for each key in `$data`.
* Each value indicates whether the key and value were successfully set.
*/
function wp_cache_add_multiple( array $data, $group = '', $expire = 0 ) {
global $wp_object_cache;
return $wp_object_cache->add_multiple( $data, $group, $expire );
}
/**
* Replaces multiple key-value pairs in the cache.
*
* @since 5.5.0
*
* @see WP_Object_Cache::replace_multiple()
*
* @param array $data An array of key-value pairs to replace. Each key is the cache key, and the value is the data to be cached.
* @param string $group Optional. The group to add the cache to. Allows for ungrouped cache values.
* Default empty string.
* @param int $expire Optional. When the cache data should expire, in seconds.
* Default 0 (no expiration).
* @return bool[] An array of booleans, one for each key in `$data`.
* Each value indicates whether the key and value were successfully replaced.
*/
function wp_cache_replace_multiple( array $data, $group = '', $expire = 0 ) {
global $wp_object_cache;
return $wp_object_cache->replace_multiple( $data, $group, $expire );
}
/**
* Sets multiple key-value pairs in the cache.
*
* @since 5.5.0
*
* @see WP_Object_Cache::set_multiple()
*
* @param array $data An array of key-value pairs to set. Each key is the cache key, and the value is the data to be cached.
* @param string $group Optional. The group to add the cache to. Allows for ungrouped cache values.
* Default empty string.
* @param int $expire Optional. When the cache data should expire, in seconds.
* Default 0 (no expiration).
* @return bool[] An array of booleans, one for each key in `$data`.
* Each value indicates whether the key and value were successfully set.
*/
function wp_cache_set_multiple( array $data, $group = '', $expire = 0 ) {
global $wp_object_cache;
return $wp_object_cache->set_multiple( $data, $group, $expire );
}
/**
* Gets multiple values from the cache in one call.
*
* @since 5.5.0
*
* @see WP_Object_Cache::get_multiple()
*
* @param array $keys An array of keys to retrieve from the cache.
* @param string $group Optional. The group to add the cache to. Allows for ungrouped cache values.
* Default empty string.
* @param bool $force Optional. Whether to force a refresh of the local
* cache from the persistent cache. Default false.
* @return array An array of key-value pairs. Each key is the cache key, and the value is the data that was cached.
* If a key does not exist in the cache, its value will be `false`.
*/
function wp_cache_get_multiple( array $keys, $group = '', $force = false ) {
global $wp_object_cache;
return $wp_object_cache->get_multiple( $keys, $group, $force );
}
/**
* Deletes multiple values from the cache in one call.
*
* @since 5.5.0
*
* @see WP_Object_Cache::delete_multiple()
*
* @param array $keys An array of keys to be deleted from the cache.
* @param string $group Optional. The group to add the cache to. Allows for ungrouped cache values.
* Default empty string.
* @return bool[] An array of booleans, one for each key in `$keys`.
* Each value indicates whether the key was successfully deleted.
*/
function wp_cache_delete_multiple( array $keys, $group = '' ) {
global $wp_object_cache;
return $wp_object_cache->delete_multiple( $keys, $group );
}
这些函数内部都使用了全局变量 $wp_object_cache
。这个变量存储了 WP_Object_Cache
类的实例。在WordPress启动的时候,会根据配置加载不同的缓存后端,并创建对应的 WP_Object_Cache
子类的实例,赋值给 $wp_object_cache
。
缓存组 (Groups)
缓存组的概念是为了更好地组织和管理缓存数据。你可以把不同类型的数据放到不同的组里。比如,可以创建一个 posts
组来存储文章数据,一个 users
组来存储用户数据。
在调用 wp_cache_add()
、wp_cache_get()
、wp_cache_set()
等函数时,可以指定一个组名。这样,缓存后端就可以根据组名来存储和检索数据。
缓存后端的选择和配置
WordPress 默认使用 WP_Object_Cache
类提供的内存缓存。但这种缓存只在单个请求的生命周期内有效,无法在多个请求之间共享。
为了实现持久化缓存,可以使用其他的缓存后端,比如:
- Memcached: 一个高性能的分布式内存对象缓存系统。
- Redis: 一个开源的内存数据结构存储系统,可以用作数据库、缓存和消息代理。
- APCu: PHP的一个用户缓存扩展,提供进程级别的缓存。
要使用这些缓存后端,需要安装对应的 PHP 扩展,并配置 WordPress。通常,需要在 wp-config.php
文件中定义 WP_CACHE_KEY_SALT
常量,并引入对应的缓存配置文件(比如 object-cache.php
)。
一个简单的缓存使用例子
假设我们要缓存一个文章对象。可以这样做:
<?php
// 假设我们已经获取了文章对象 $post
$post_id = $post->ID;
$cache_key = 'post_' . $post_id;
$group = 'posts';
// 尝试从缓存中获取文章对象
$cached_post = wp_cache_get( $cache_key, $group );
if ( $cached_post ) {
// 如果缓存中存在,直接使用缓存的数据
$post = $cached_post;
echo "从缓存中获取文章数据";
} else {
// 如果缓存中不存在,从数据库中获取数据
// 这里省略从数据库获取数据的代码
// 获取数据后,将数据保存到缓存中
wp_cache_set( $cache_key, $post, $group, 3600 ); // 缓存 3600 秒
echo "从数据库中获取文章数据,并保存到缓存";
}
// 使用 $post 对象显示文章内容
echo "文章标题:" . $post->post_title;
?>
缓存的最佳实践
- 选择合适的缓存后端: 根据网站的规模和需求,选择合适的缓存后端。对于小型网站,APCu 可能就足够了。对于大型网站,Memcached 或 Redis 可能更适合。
- 合理设置缓存过期时间: 不要把所有数据都永久缓存,要根据数据的更新频率,合理设置缓存过期时间。
- 使用缓存组: 使用缓存组可以更好地组织和管理缓存数据,方便清理和维护。
- 监控缓存命中率: 定期监控缓存命中率,如果命中率过低,可能需要调整缓存策略。
- 小心缓存雪崩: 避免大量缓存同时失效,导致数据库压力过大。可以使用随机过期时间或者二级缓存等策略来缓解缓存雪崩。
总结
wp-includes/cache.php
文件定义了 WordPress 的 Object Cache 接口。通过这组接口,WordPress 可以