利用Redis Object Cache缓存WordPress慢查询,优化Sphere插件MySQL负载
一、用Redis Object Cache缓存该慢查询的可行性
可以借助Redis缓存降低该查询的CPU消耗,但不能直接缓存原始SQL语句的执行结果——Redis Object Cache主要针对WordPress的对象(如WP_Post实例、WP_Query结果集)进行缓存。具体实现思路:
- 手动缓存子查询统计结果:把
SELECT SUM(pageviews) AS pageviews, postid FROM wp_popularpostssummary WHERE view_date >= 'xxx' GROUP BY postid的结果用wp_cache_set()存储,缓存过期时间设为5-15分钟(根据实时性需求调整),每次获取浏览量时先从Redis取,不存在再执行数据库查询。 - 让WP_Query结果被Redis缓存:确保自定义查询通过WordPress标准的WP_Query类执行,Redis Object Cache会自动缓存符合条件的查询结果(前提是查询参数可哈希)。如果插件直接执行原生SQL,需修改逻辑转为WP_Query调用,或手动封装缓存逻辑。
二、查询语句的优化方案
你提供的查询存在几个明显性能瓶颈,以下是针对性优化:
移除
SQL_CALC_FOUND_ROWS
若不需要获取符合条件的帖子总条数(比如分页不需要总页数),直接删除该语句——它会强制MySQL扫描所有符合条件的行,大幅增加CPU开销。若必须要总数,单独执行SELECT COUNT(ID) FROM wp_posts WHERE post_type = 'post' AND post_status = 'publish',效率远高于SQL_CALC_FOUND_ROWS。添加联合索引加速子查询
子查询是性能核心瓶颈,给wp_popularpostssummary表创建联合索引:CREATE INDEX idx_viewdate_postid_pageviews ON wp_popularpostssummary (view_date, postid, pageviews);这个索引可让MySQL直接通过索引完成
WHERE view_date >= ...、GROUP BY postid和SUM(pageviews)的计算,无需全表扫描。保持
SELECT字段精简
你通过posts_fields过滤器修改返回字段的做法非常好,继续保持——只查询需要的字段(ID、post_title、post_content片段、post_name、post_date),减少数据传输和内存占用。优化ORDER BY逻辑
外层查询的ORDER BY post_views DESC, wp_posts.post_date DESC需对结果集排序,可尝试:- 给
wp_posts表添加索引:CREATE INDEX idx_type_status_date ON wp_posts (post_type, post_status, post_date DESC);,加速WHERE条件过滤和post_date排序。 - 若业务允许,限制返回的帖子数量(比如首页只显示20条),减少排序的数据量。
- 给
缩小时间范围
如果业务允许,把view_date >= '2023-08-30'改为更短的时间范围(比如最近7天),子查询的计算量会大幅降低,同时缓存有效性也会提升。
三、对你现有优化函数的调整建议
你的modify_posts_fields函数存在几处错误,修正后才能正常生效:
- 修正字段拼接语法:把
$wpdb->prefix . 'http://posts.ID'改为$wpdb->posts . '.ID'(或$wpdb->prefix . 'posts.ID'),去掉多余的http://。 - 调整查询条件:
!$query->is_main_query()改为$query->is_main_query(),因为首页的主查询才是你需要优化的目标,否则条件不成立,过滤器不会生效。 - 可加入缓存逻辑:在获取浏览量统计时先从Redis读取,示例:
/*******Query change - Hook the callback function to the 'posts_fields' filter ******/ function modify_posts_fields($fields, $query) { global $wpdb; // 仅针对首页的SPV主查询生效 if ($query->is_main_query() && !is_admin() && isset($query->spv_query) && $query->spv_query && is_front_page()) { $fields = "{$wpdb->posts}.ID, {$wpdb->posts}.post_title, LEFT({$wpdb->posts}.post_content, 200) AS post_content, {$wpdb->posts}.post_name, {$wpdb->posts}.post_date"; } return $fields; } add_filter('posts_fields', 'modify_posts_fields', 10, 2); // 额外添加:缓存浏览量统计结果 function get_spv_post_views($start_date) { $cache_key = "spv_post_views_{$start_date}"; $views = wp_cache_get($cache_key); if (false === $views) { global $wpdb; $views = $wpdb->get_results($wpdb->prepare("SELECT SUM(pageviews) AS pageviews, postid FROM {$wpdb->prefix}popularpostssummary WHERE view_date >= %s GROUP BY postid", $start_date), ARRAY_A); wp_cache_set($cache_key, $views, '', 900); // 缓存15分钟 } return $views; }
内容的提问来源于stack exchange,提问作者user102195

