A-
A+
WordPress创建自定义数据库表 用于缓存自己创建的自定义缓存字段方便清理
最近,在改造我的网站,把数据库里的缓存想从wp_options分离出来,使用单独的数据表缓存,避免时间久了不好清理。
于是我就搭建了一个数据库分表,名字设置为cache
创建数据库的SQL语句代码如下:
CREATE TABLE `cache` ( `id` bigint(20) UNSIGNED NOT NULL AUTO_INCREMENT COMMENT '自增主键', `cache_key` varchar(255) NOT NULL COMMENT '缓存键名', `cache_value` longtext NOT NULL COMMENT '序列化缓存内容', `expire_time` bigint(20) UNSIGNED NOT NULL COMMENT '过期时间戳,0=永久有效', `create_time` bigint(20) UNSIGNED NOT NULL COMMENT '创建时间戳', PRIMARY KEY (`id`), UNIQUE KEY `uk_cache_key` (`cache_key`), KEY `idx_expire` (`expire_time`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='自定义缓存兜底表';
然后在模版里创建cache.php,代码如下:
<?php
/**
* cache 独立缓存函数库 用于缓存归档页面和顶部菜单
* 共用缓存表 cache
* 双层缓存:内存优先 + MySQL持久兜底
*/
if (!defined('ABSPATH')) {
exit;
}
if (!defined('CACHE_TABLE')) {
define('CACHE_TABLE', 'cache');
}
/**
* 获取缓存
*/
function cache_get($cache_key) {
global $wpdb;
$table = CACHE_TABLE;
$now = time();
$mem_key = 'cache_' . $cache_key;
// 第一层:优先读取内存缓存
$cache_data = wp_cache_get($mem_key);
if ($cache_data !== false) {
return $cache_data;
}
// 【移除】惰性随机清理,高并发容易造成大量DELETE锁表,改用MySQL Event定时清理
// if (mt_rand(1, 10000) <= 1) cache_clean_expired();
$row = $wpdb->get_row($wpdb->prepare("SELECT cache_value FROM `$table` WHERE cache_key = %s AND (expire_time = 0 OR expire_time > %d)", $cache_key, $now));
if(!$row) return false;
/*上面惰性清理的美化版
// 低概率惰性清理垃圾缓存
if (mt_rand(1, 10000) <= 1) {
cache_clean_expired();
}
$row = $wpdb->get_row(
$wpdb->prepare(
"SELECT cache_value FROM `$table` WHERE cache_key = %s AND (expire_time = 0 OR expire_time > %d)",
$cache_key,
$now
)
);
if (empty($row)) {
return false;
} */
// 增加反序列化容错,避免损坏缓存导致报错
$data = @unserialize($row->cache_value);
if ($data === false && $row->cache_value !== 'b:0;') {
return false;
}
// 回填内存缓存,内存TTL300秒,短于数据库TTL
wp_cache_set($mem_key, $data, '', 300);
return $data;
}
/**
* 设置缓存(INSERT ON DUPLICATE KEY 替代REPLACE)
*/
function cache_set($cache_key, $data, $ttl = 3600) {
global $wpdb;
$table = CACHE_TABLE;
$now = time();
$expire = $ttl > 0 ? $now + $ttl : 0;
$val = serialize($data);
$mem_key = 'cache_' . $cache_key;
$sql = $wpdb->prepare(
"INSERT INTO `$table` (cache_key, cache_value, expire_time, create_time)
VALUES (%s, %s, %d, %d)
ON DUPLICATE KEY UPDATE
cache_value = %s,
expire_time = %d",
$cache_key,
$val,
$expire,
$now,
$val,
$expire
);
$res = $wpdb->query($sql);
// 同步写入内存缓存
wp_cache_set($mem_key, $data, '', $ttl);
return $res;
}
/**
* 删除单个缓存
*/
function cache_delete($cache_key) {
global $wpdb;
$table = CACHE_TABLE;
$mem_key = 'cache_' . $cache_key;
wp_cache_delete($mem_key);
return $wpdb->delete($table, ['cache_key'=>$cache_key], ['%s']);
}
/**
* 按前缀批量删除缓存
* 注意:wp_cache原生不支持前缀批量删除内存缓存,内存只能等待TTL自动过期
*/
function cache_delete_prefix($prefix) {
global $wpdb;
$table = CACHE_TABLE;
$like = $wpdb->esc_like($prefix) . '%';
return $wpdb->query($wpdb->prepare("DELETE FROM `$table` WHERE cache_key LIKE %s", $like));
}
/**
* 清理过期缓存(仅保留函数,不再自动触发,交由MySQL Event调用)
*/
function cache_clean_expired() {
global $wpdb;
$table = CACHE_TABLE;
$now = time();
return $wpdb->query($wpdb->prepare("DELETE FROM `$table` WHERE expire_time > 0 AND expire_time < %d LIMIT 200", $now));
}
/**
* 文章状态变更清理归档缓存(替代save_post,更精准)
*/
add_action('transition_post_status', function($new_status, $old_status, $post) {
// 跳过自动保存、修订
if(wp_is_post_autosave($post->ID) || wp_is_post_revision($post->ID)) return;
if ($post->post_type !== 'post') return;
// 只有【发布 ↔ 其他状态】变动才处理
if ($new_status !== 'publish' && $old_status !== 'publish') return;
$now_year = date('Y');
$post_year = get_the_time('Y', $post->ID);
// 策略:只修改当年文章清理缓存,往年文章改动不清理
if ($post_year !== $now_year) {
return;
}
cache_delete_prefix('archive_year_single_' . $now_year);
cache_delete('archive_year_list_global');
cache_delete('archive_cat_num');
cache_delete('archive_tag_num');
cache_delete('archive_post_num');
cache_delete('archive_comment_count');
cache_delete('archive_last_update');
}, 10, 3);
// 删除文章同步清理
add_action('delete_post', function($post_id){
$post = get_post($post_id);
if(!$post || $post->post_type !== 'post') return;
$now_year = date('Y');
$post_year = get_the_time('Y', $post_id);
if ($post_year !== $now_year) return;
cache_delete_prefix('archive_year_single_' . $now_year);
cache_delete('archive_year_list_global');
cache_delete('archive_cat_num');
cache_delete('archive_tag_num');
cache_delete('archive_post_num');
cache_delete('archive_comment_count');
cache_delete('archive_last_update');
});
// 评论、分类变更 → 清空全部归档缓存
$clear_archive_global = function(){
cache_delete_prefix('archive_year_single_');
cache_delete('archive_cat_num');
cache_delete('archive_tag_num');
cache_delete('archive_post_num');
cache_delete('archive_comment_count');
cache_delete('archive_last_update');
cache_delete('archive_year_list_global');
};
add_action('comment_approved', $clear_archive_global);
add_action('comment_unapproved', $clear_archive_global);
add_action('wp_set_comment_status', $clear_archive_global);
add_action('edited_term', $clear_archive_global);
最后在functions.php里引入这个cache.php就可以缓存到cache这个单独数据表了。
// 加载独立缓存函数 require_once get_template_directory() . '/inc/cache.php';
上面的代码内存缓存,我使用的memcached,redis应该也有效,建议自行测试。

