当WordPress网站数据量达到数十万甚至百万级别时,传统的相关文章查询(特别是使用ORDER BY RAND())会导致服务器高负载、数据库查询缓慢,严重影响网站性能。我使用的dux主题,以此为背景。

核心问题分析
原相关文章代码主要问题:
-
使用
ORDER BY RAND()导致全表扫描,性能极差 -
缺少有效缓存机制,每次访问都查询数据库
-
复杂的标签+分类查询逻辑,查询次数过多
解决方案
一、优化后的相关文章代码
将以下代码替换原文件:wp-content/themes/dux/modules/mo_posts_related.php
<?php
/**
* 高性能相关文章函数 - 使用文件缓存
* 适用于百万数据量的WordPress网站
*/
function mo_posts_related($title = '相关阅读', $limit = 6) {
$setting = _hui('post_related');
if ($setting == 'off') return false;
global $post;
// 使用文件缓存替代数据库查询
$cache_dir = WP_CONTENT_DIR . '/cache/related_posts/';
if (!file_exists($cache_dir)) {
wp_mkdir_p($cache_dir);
}
$cache_file = $cache_dir . 'post_' . $post->ID . '.html';
$cache_expire = 6 * HOUR_IN_SECONDS;
// 检查文件缓存是否存在且未过期
if (file_exists($cache_file) && (time() - filemtime($cache_file)) < $cache_expire) {
echo file_get_contents($cache_file);
return;
}
// 缓存不存在或已过期,执行查询
$thumb_s = $setting == 'imagetext';
$cache_data = '<div class="relates relates-' . $setting . '"><div class="title"><h3>' . $title . '</h3></div><ul>';
$final_post_ids = array();
// 第一步:标签相关文章(按时间排序,高性能)
$posttags = get_the_tags($post->ID);
if ($posttags && !_hui('post_related_cat_only')) {
$tag_ids = array();
foreach ($posttags as $tag) {
$tag_ids[] = $tag->term_id;
}
$tag_args = array(
'tag__in' => $tag_ids,
'post__not_in' => array($post->ID),
'posts_per_page' => $limit,
'ignore_sticky_posts' => 1,
'orderby' => 'date',
'no_found_rows' => true,
'fields' => 'ids'
);
$tag_query = new WP_Query($tag_args);
$final_post_ids = $tag_query->posts;
wp_reset_postdata();
}
$found_count = count($final_post_ids);
// 第二步:如果标签文章不足,用同分类最新文章补齐
if ($found_count < $limit) {
$needed = $limit - $found_count;
$categories = get_the_category($post->ID);
$category_ids = array();
foreach ($categories as $category) {
$category_ids[] = $category->term_id;
}
if (!empty($category_ids)) {
$category_args = array(
'category__in' => $category_ids,
'post__not_in' => array_merge(array($post->ID), $final_post_ids),
'posts_per_page' => $needed,
'ignore_sticky_posts' => 1,
'orderby' => 'date',
'no_found_rows' => true,
'fields' => 'ids'
);
$category_query = new WP_Query($category_args);
$category_ids_result = $category_query->posts;
$final_post_ids = array_merge($final_post_ids, $category_ids_result);
wp_reset_postdata();
}
}
// 第三步:如果还不够,用全站最新文章补齐
$final_count = count($final_post_ids);
if ($final_count < $limit) {
$still_needed = $limit - $final_count;
$fallback_args = array(
'post__not_in' => array_merge(array($post->ID), $final_post_ids),
'posts_per_page' => $still_needed,
'ignore_sticky_posts' => 1,
'orderby' => 'date',
'no_found_rows' => true,
'fields' => 'ids'
);
$fallback_query = new WP_Query($fallback_args);
$fallback_ids = $fallback_query->posts;
$final_post_ids = array_merge($final_post_ids, $fallback_ids);
wp_reset_postdata();
}
// 第四步:显示文章
if (!empty($final_post_ids)) {
$final_args = array(
'post__in' => $final_post_ids,
'orderby' => 'post__in',
'posts_per_page' => $limit,
'ignore_sticky_posts' => 1,
'no_found_rows' => true
);
$final_query = new WP_Query($final_args);
while ($final_query->have_posts()) {
$final_query->the_post();
$cache_data .= '<li>';
if ($thumb_s) $cache_data .= '<a' . _post_target_blank() . ' href="' . get_permalink() . '">' . _get_post_thumbnail() . '</a>';
$cache_data .= '<a href="' . get_permalink() . '">' . get_the_title() . get_the_subtitle() . '</a></li>';
}
wp_reset_postdata();
} else {
$cache_data .= '<li>暂无相关文章</li>';
}
$cache_data .= '</ul></div>';
// 保存到文件缓存
file_put_contents($cache_file, $cache_data);
// 输出
echo $cache_data;
}
?>
二、批量部署命令
1. 为所有站点创建缓存目录并设置权限
#!/bin/bash # create_cache_dirs.sh - 创建缓存目录并设置权限 echo "=== 为所有站点创建缓存目录 ===" for site in /home/www/*; do if [ -f "$site/wp-config.php" ]; then site_name=$(basename "$site") # 创建缓存目录 cache_dir="$site/wp-content/cache/related_posts/" mkdir -p "$cache_dir" # 设置权限 chown -R www:www "$site/wp-content/cache/" chmod -R 755 "$site/wp-content/cache/" echo "✓ 已创建: $site_name" fi done echo "=== 完成 ==="
2. 手动触发缓存生成
#!/bin/bash # generate_initial_cache.sh - 手动触发初始缓存生成 echo "=== 手动生成初始缓存 ===" echo "为每个站点的最新文章生成缓存" for site in /home/www/*; do if [ -f "$site/wp-config.php" ]; then site_name=$(basename "$site") echo "处理站点: $site_name" # 获取数据库名 db_name=$(grep DB_NAME "$site/wp-config.php" | head -1 | cut -d"'" -f4) if [ ! -z "$db_name" ]; then # 获取最新的5篇文章 post_ids=$(mysql -u root -p -e " SELECT ID FROM \`$db_name\`.wp_posts WHERE post_status='publish' AND post_type='post' ORDER BY ID DESC LIMIT 5 " 2>/dev/null | tail -n +2) count=0 for post_id in $post_ids; do if [ ! -z "$post_id" ]; then echo " 生成文章 $post_id 的缓存" curl -s -o /dev/null "http://$site_name/?p=$post_id" & count=$((count + 1)) sleep 1 # 避免压力过大 fi done echo "✓ $site_name: 已触发 $count 篇文章缓存生成" fi echo fi done echo "等待缓存生成完成..." wait echo "=== 初始缓存生成完成 ==="
3. 检查缓存是否生效
#!/bin/bash
# check_cache_status.sh - 检查缓存状态
echo "=== 缓存状态检查 ==="
echo "检查时间: $(date '+%Y-%m-%d %H:%M:%S')"
echo
total_files=0
total_sites=0
active_sites=0
for site in /home/www/*; do
if [ -d "$site/wp-content/cache/related_posts/" ]; then
site_name=$(basename "$site")
total_sites=$((total_sites + 1))
cache_count=$(find "$site/wp-content/cache/related_posts/" -name "*.html" 2>/dev/null | wc -l)
if [ "$cache_count" -gt 0 ]; then
active_sites=$((active_sites + 1))
total_files=$((total_files + cache_count))
echo "✓ $site_name: $cache_count 个缓存文件"
# 显示最新缓存
latest_file=$(find "$site/wp-content/cache/related_posts/" -name "*.html" -printf "%T@ %p\n" 2>/dev/null | sort -rn | head -1)
if [ ! -z "$latest_file" ]; then
file_time=$(echo "$latest_file" | awk '{print strftime("%m-%d %H:%M", $1)}')
file_name=$(echo "$latest_file" | awk '{print $2}' | xargs basename)
echo " 最新: $file_name ($file_time)"
fi
else
echo "✗ $site_name: 0个缓存文件"
fi
fi
done
echo
echo "=== 统计汇总 ==="
echo "总站点数: $total_sites"
echo "有缓存的站点: $active_sites"
echo "总缓存文件数: $total_files"
echo "平均每个站点: $(echo "scale=1; $total_files/$total_sites" | bc) 个缓存文件"
三、缓存清理与维护
1. 创建缓存清理脚本
cat > /root/clean_related_cache.sh << 'CLEAN_SCRIPT'
#!/bin/bash
# clean_related_cache.sh - 清理过期缓存
# 保存到: /root/clean_related_cache.sh
PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin
LOG_FILE="/www/wwwlogs/clean_related_cache.log"
# 同时输出到屏幕和日志的函数
log() {
echo "$1"
echo "[$(date '+%Y-%m-%d %H:%M:%S')] $1" >> "$LOG_FILE"
}
echo "========================================"
echo "清理过期的相关文章缓存"
echo "执行时间: $(date '+%Y-%m-%d %H:%M:%S')"
echo "========================================"
# 同时记录到日志
echo "========================================" >> "$LOG_FILE"
echo "清理开始: $(date '+%Y-%m-%d %H:%M:%S')" >> "$LOG_FILE"
echo "========================================" >> "$LOG_FILE"
total_deleted=0
total_sites=0
# 清理所有站点的相关文章缓存
for site_path in /home/www/*; do
if [ -d "$site_path" ]; then
site_name=$(basename "$site_path")
cache_dir="$site_path/wp-content/cache/related_posts/"
if [ -d "$cache_dir" ]; then
total_sites=$((total_sites + 1))
before_count=$(find "$cache_dir" -name "*.html" 2>/dev/null | wc -l)
if [ "$before_count" -gt 0 ]; then
# 删除超过7天的缓存
deleted_count=$(find "$cache_dir" -name "*.html" -mtime +7 | wc -l)
if [ "$deleted_count" -gt 0 ]; then
find "$cache_dir" -name "*.html" -mtime +7 -delete 2>/dev/null
fi
total_deleted=$((total_deleted + deleted_count))
after_count=$(find "$cache_dir" -name "*.html" 2>/dev/null | wc -l)
log "[INFO] $site_name: 删除 $deleted_count 个旧缓存,剩余 $after_count 个"
else
log "[INFO] $site_name: 无缓存文件"
fi
else
log "[WARN] $site_name: 缓存目录不存在"
fi
fi
done
echo "========================================"
echo "清理完成"
echo "处理站点数: $total_sites"
echo "删除缓存文件: $total_deleted 个"
echo "完成时间: $(date '+%Y-%m-%d %H:%M:%S')"
echo "========================================"
# 同时记录到日志
echo "========================================" >> "$LOG_FILE"
echo "清理完成" >> "$LOG_FILE"
echo "处理站点数: $total_sites" >> "$LOG_FILE"
echo "删除缓存文件: $total_deleted 个" >> "$LOG_FILE"
echo "完成时间: $(date '+%Y-%m-%d %H:%M:%S')" >> "$LOG_FILE"
echo "========================================" >> "$LOG_FILE"
CLEAN_SCRIPT
# 给执行权限
chmod +x /root/clean_related_cache.sh
# 验证文件已创建
echo "=== 验证文件创建 ==="
ls -la /root/clean_related_cache.sh
echo "文件行数: $(wc -l < /root/clean_related_cache.sh)"
# 测试运行
echo
echo "=== 测试运行 ==="
/root/clean_related_cache.sh
2. 配置宝塔计划任务
在宝塔面板中添加计划任务:
-
登录宝塔面板 → 计划任务
-
点击 “添加计划任务”
-
填写以下信息:
-
任务类型:Shell脚本
-
任务名称:清理相关文章缓存
-
执行周期:每天 3:00
-
脚本内容:
/root/clean_related_cache.sh -
备注:自动清理超过7天的相关文章缓存文件
-
-
点击 “添加任务”
-
可点击 “执行” 按钮立即测试
性能优化效果
优化前后对比
| 指标 | 优化前 | 优化后 | 提升倍数 |
|---|---|---|---|
| 单次查询时间 | 2-5秒 | 10-100ms | 50倍 |
| 并发处理能力 | 10 req/s | 500+ req/s | 50倍 |
| CPU占用 | 单核100% | <5% | 20倍 |
| 数据库查询 | 每次访问都查询 | 6小时查询一次 | 数千倍 |
缓存机制优势
-
首次访问:查询数据库,生成HTML缓存文件
-
后续访问:直接读取缓存文件,0数据库查询
-
并发访问:所有用户共享同一缓存文件
-
缓存有效期:6小时自动更新
部署检查清单
-
备份原相关文章代码文件
-
替换为新优化代码
-
创建缓存目录并设置权限
-
生成初始缓存
-
验证缓存生效
-
配置自动清理任务
故障排除
常见问题及解决方案
-
缓存文件未生成
-
检查目录权限:
chown -R www:www /home/www/*/wp-content/cache/ -
检查PHP错误日志
-
-
负载未下降
-
确认代码已正确替换
-
检查是否有其他性能瓶颈(如采集器、导出功能)
-
观察缓存文件数量是否增长
-
-
宝塔任务不执行
-
检查脚本路径是否正确
-
检查脚本执行权限:
chmod +x /root/clean_related_cache.sh -
查看宝塔任务日志
-
注意事项
-
磁盘空间:定期清理过期缓存,避免占用过多空间
-
权限设置:确保缓存目录对Web用户可写
-
代码兼容:确保主题函数
_hui()、_post_target_blank()等存在 -
多站点部署:批量操作时注意站点路径差异
监控指标
部署后应监控以下指标:
-
服务器负载平均值(
uptime) -
缓存文件数量增长
-
MySQL慢查询数量
-
PHP-FPM进程数和CPU使用率
适用环境: WordPress + 宝塔面板 + 十万级以上数据
效果验证: 实测负载从10+降至2.0以下







.png)
.jpg)



.png)
.png)

.png)

评论前必须登录!
注册