严潇健
互联网引流变现最靓的仔

WordPress百万数据优化:相关文章高性能解决方案

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

核心问题分析

原相关文章代码主要问题:

  1. 使用 ORDER BY RAND() 导致全表扫描,性能极差

  2. 缺少有效缓存机制,每次访问都查询数据库

  3. 复杂的标签+分类查询逻辑,查询次数过多

解决方案

一、优化后的相关文章代码

将以下代码替换原文件: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. 配置宝塔计划任务

在宝塔面板中添加计划任务:

  1. 登录宝塔面板 → 计划任务

  2. 点击 “添加计划任务”

  3. 填写以下信息:

    • 任务类型:Shell脚本

    • 任务名称:清理相关文章缓存

    • 执行周期:每天 3:00

    • 脚本内容/root/clean_related_cache.sh

    • 备注:自动清理超过7天的相关文章缓存文件

  4. 点击 “添加任务”

  5. 可点击 “执行” 按钮立即测试

性能优化效果

优化前后对比

指标 优化前 优化后 提升倍数
单次查询时间 2-5秒 10-100ms 50倍
并发处理能力 10 req/s 500+ req/s 50倍
CPU占用 单核100% <5% 20倍
数据库查询 每次访问都查询 6小时查询一次 数千倍

缓存机制优势

  1. 首次访问:查询数据库,生成HTML缓存文件

  2. 后续访问:直接读取缓存文件,0数据库查询

  3. 并发访问:所有用户共享同一缓存文件

  4. 缓存有效期:6小时自动更新

部署检查清单

  1. 备份原相关文章代码文件

  2. 替换为新优化代码

  3. 创建缓存目录并设置权限

  4. 生成初始缓存

  5. 验证缓存生效

  6. 配置自动清理任务

故障排除

常见问题及解决方案

  1. 缓存文件未生成

    • 检查目录权限:chown -R www:www /home/www/*/wp-content/cache/

    • 检查PHP错误日志

  2. 负载未下降

    • 确认代码已正确替换

    • 检查是否有其他性能瓶颈(如采集器、导出功能)

    • 观察缓存文件数量是否增长

  3. 宝塔任务不执行

    • 检查脚本路径是否正确

    • 检查脚本执行权限:chmod +x /root/clean_related_cache.sh

    • 查看宝塔任务日志

注意事项

  1. 磁盘空间:定期清理过期缓存,避免占用过多空间

  2. 权限设置:确保缓存目录对Web用户可写

  3. 代码兼容:确保主题函数_hui()_post_target_blank()等存在

  4. 多站点部署:批量操作时注意站点路径差异

监控指标

部署后应监控以下指标:

  • 服务器负载平均值(uptime

  • 缓存文件数量增长

  • MySQL慢查询数量

  • PHP-FPM进程数和CPU使用率


适用环境: WordPress + 宝塔面板 + 十万级以上数据
效果验证: 实测负载从10+降至2.0以下

评论 抢沙发

评论前必须登录!

 

登录

找回密码

注册