租号玩代理申请
A- A+

WordPress创建自定义数据库表 用于缓存自己创建的自定义缓存字段方便清理

2026年05月09日 建站相关 暂无评论 阅读 53 次 打印

最近,在改造我的网站,把数据库里的缓存想从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应该也有效,建议自行测试。

给我留言

8 - 1 =
评论规范提醒