WordPress站点统计实战:函数/SQL/Transient/REST/Redis五层方案与WP6.x API变化

WordPress 主题里展示文章数、评论数、用户数、字数等统计数据?网传代码大多写于 WP 4.x 之前,wp_count_terms 返回值在 WP 5.0+ 已变、Links Manager 默认下线、未缓存的 wp_count_posts 在大站会拖慢 TTFB。本文给出 WP 6.x 兼容的内置函数、$wpdb 直查、Transient 缓存、REST API 输出、Redis Object Cache、WooCommerce HPOS 与多站点扩展统计的全套方案,附性能基准与 FAQ。

张文保 更新 31 分钟阅读 1,888 阅读
本文目录
  1. 最常用的内置函数与 SQL 调用
  2. 文章数量(不同 post_type)
  3. 分类与标签数量(WP 5.0 后的 API 变化)
  4. 评论数量
  5. 用户数量
  6. 建站时长(最早一篇文章为基准而不是固定日期)
  7. SQL 直查的真实使用场景
  8. 全站总字数(所有已发布文章 post_content 累加)
  9. 总浏览量(如果你存了 view_count 自定义字段)
  10. 最后更新时间
  11. Transient API:性能 100x 优化
  12. 性能基准
  13. 何时清缓存
  14. 用 REST API 拉统计(前端 / 跨站调用)
  15. 自定义统计端点
  16. WooCommerce 与多站点的扩展统计
  17. WooCommerce 订单 / 销售额
  18. WordPress 多站点(Multisite)的全局统计
  19. 文章浏览量(PV)的几种实现
  20. 自己写一个简单计数器
  21. 用插件代替
  22. 用外部分析工具
  23. 性能压测:实际项目中的统计开销
  24. 几个不值得自己写的扩展统计
  25. 自定义文章类型 (CPT) 的统计
  26. 常见问题解答
  27. wp_count_posts 在大站为什么慢?怎么救?
  28. wp_count_terms 在 WP 5.0 之后到底返回什么?
  29. 站点统计能不能不写 PHP,纯前端 JS 实现?
  30. 多语言站(WPML / Polylang)的文章数怎么统计?
  31. 用 Redis Object Cache 之后 transient 失效不及时怎么办?
  32. WooCommerce HPOS 模式下,老的 SQL 查询订单怎么改?
  33. 建站时间用文章最早时间,但我有些早期文章是导入的、时间错了怎么办?
  34. 分享统计数据到搜索引擎的 sitemap.xml 有意义吗?
  35. 不同 hook 时机统计数会有偏差吗?
  36. 有没有可视化的"WordPress 后台仪表板"做这种统计?

WordPress 主题开发或前台展示页面常需要拉一些"全站统计"数据:发了多少篇文章、有几条评论、网站建站多久了、注册了多少用户。WordPress 自带一组函数和 $wpdb 全局对象能搞定这事,但网上流传的代码片段大多写于 WordPress 4.x 之前,在 WP 6.x 上有几处坑:wp_count_terms 返回值类型变了、Links Manager 默认下线、频繁调用 wp_count_posts 在百万级文章站会拖慢响应。

这一篇把 WordPress 站点统计这件事重新梳理:函数级统计、SQL 级统计、Transient 缓存、REST API、Object Cache、WooCommerce / 多站点扩展逐层讲清,给出 2026 年仍能跑的代码、性能基准、缓存策略、踩过的坑与 FAQ。

最常用的内置函数与 SQL 调用

文章数量(不同 post_type)

wp_count_posts() 是 WordPress 自带的核心函数,按 post_type 分别统计各种状态(publish / draft / pending / trash 等)的数量:

// 已发布文章数
$counts = wp_count_posts();         // 默认 post_type = 'post'
echo (int)$counts->publish;          // 已发布
echo (int)$counts->draft;            // 草稿
echo (int)$counts->trash;            // 回收站
echo (int)$counts->pending;          // 待审

// 已发布页面数
$pages = wp_count_posts('page');
echo (int)$pages->publish;

// 自定义 post type(比如 WooCommerce 的产品)
$products = wp_count_posts('product');
echo (int)$products->publish;

注意点:

  • 返回值是 stdClass 对象,不是数组——必须用 -> 访问属性。
  • 访问不存在的状态会返回 null,强制转 int 兜底((int)$counts->auto-draft ?? 0)。
  • 被回收的文章 trash 算独立状态,不会被 publish 包含。

分类与标签数量(WP 5.0 后的 API 变化)

wp_count_terms() 在 WordPress 5.0 之前返回纯整数,5.0 之后改成返回字符串或 WP_Error 对象,老代码 echo $count; 直接 echo 一个 WP_Error 会报"Object of class WP_Error could not be converted to string"或得到一个不准的值。

// ❌ 老代码(WP 5.0+ 不靠谱)
echo $count = wp_count_terms('post_tag');

// ✅ WP 5.0+ 推荐:用 get_terms 加 fields=count,或 wp_count_terms 加 hide_empty
$tag_count = wp_count_terms([
    'taxonomy'   => 'post_tag',
    'hide_empty' => false,    // 包含没文章的标签
]);
echo is_wp_error($tag_count) ? 0 : (int)$tag_count;

// 或者用 get_terms 配 'fields' => 'count'(WP 4.5+)
$tag_count = count(get_terms([
    'taxonomy'   => 'post_tag',
    'hide_empty' => false,
    'fields'     => 'ids',    // 只取 ID,节省内存
]));

hide_empty 是关键参数:默认 true(只算有文章的标签),如果你想要"标签库总数"应改为 false。这一条决定了一些导航站会出现"标签云里写 50 个标签,但侧边栏写'共有 30 个标签'"的尴尬不一致。

评论数量

// 全部评论(含未审、垃圾、回收站)
echo wp_count_comments()->total_comments;

// 已审通过的
echo (int)wp_count_comments()->approved;

// 垃圾箱里的
echo (int)wp_count_comments()->spam;

// 待审
echo (int)wp_count_comments()->moderated;

这个函数比 $wpdb->get_var("SELECT COUNT(*) FROM ...") 强的地方在于内部已经做了 transient 缓存(默认 1 小时),不会每次调用都打数据库。

用户数量

$users = count_users();
echo $users['total_users'];                   // 全部用户
echo $users['avail_roles']['administrator'];  // 管理员
echo $users['avail_roles']['subscriber'];     // 订阅者
echo $users['avail_roles']['editor'];         // 编辑

// 自定义角色(如 WooCommerce customer)
echo $users['avail_roles']['customer'] ?? 0;

count_users() 在用户量大(> 50K)时会很慢——它跑全表 GROUP BY。生产建议加 transient 缓存(见 §3)。

建站时长(最早一篇文章为基准而不是固定日期)

原帖代码硬编码了 2013-6-25。更稳妥的做法是用最早一篇已发布文章的时间作为站龄起点

global $wpdb;
$earliest = $wpdb->get_var("
    SELECT post_date
    FROM {$wpdb->posts}
    WHERE post_status = 'publish' AND post_type = 'post'
    ORDER BY post_date ASC
    LIMIT 1
");
$days = floor((time() - strtotime($earliest)) / 86400);
$years = floor($days / 365);
echo "本站已运行 {$years} 年({$days} 天)";

这个写法的优点:

  • 主题被复用到不同站点时不用每次改代码;
  • 用文章实际时间,比"安装 WordPress"那天更有意义;
  • 支持只展示年份或月份等不同精度。

SQL 直查的真实使用场景

以下几种统计 WordPress 没提供专门的函数,要直接走 $wpdb->get_var()

全站总字数(所有已发布文章 post_content 累加)

global $wpdb;
$total_chars = $wpdb->get_var("
    SELECT SUM(CHAR_LENGTH(post_content))
    FROM {$wpdb->posts}
    WHERE post_status = 'publish' AND post_type IN ('post','page')
");
echo number_format($total_chars) . " 字";

注意:

  • CHAR_LENGTH(按字符数)vs LENGTH(按字节数)。中文站要用 CHAR_LENGTH,否则一个中文字会被按 3 字节算。
  • 这个查询在 100K+ 文章的站上要 1-3 秒。必须缓存,前台页面绝不能同步等。

总浏览量(如果你存了 view_count 自定义字段)

$total_views = $wpdb->get_var("
    SELECT SUM(meta_value)
    FROM {$wpdb->postmeta}
    WHERE meta_key = 'post_views_count'
");
echo number_format((int)$total_views);

前提是你装了某种"文章浏览量"插件(WP-PostViews / Post Views Counter)。原生 WordPress 不记浏览量。

最后更新时间

$last_modified = $wpdb->get_var("
    SELECT MAX(post_modified)
    FROM {$wpdb->posts}
    WHERE post_status = 'publish' AND post_type IN ('post','page')
");
echo human_time_diff(strtotime($last_modified)) . "前";  // "3 小时前"

human_time_diff() 是 WP 自带的人性化时间差函数,比手算"距今多少分钟"省事。

Transient API:性能 100x 优化

上面所有统计函数 / SQL 查询的共同问题:每次页面访问都跑一遍数据库。一篇博客底部展示"共 528 篇文章 / 889 个标签 / 61 条评论"——三个统计如果都不缓存,每次访问就是 3 个 SQL,主题首页 + 列表页 + 文章页平均访问量大的话很快拖垮数据库。

Transient 是 WordPress 自带的简单键值缓存,本质是把数据序列化存到 wp_options 或 Object Cache(如装了 Redis)。用法:

function cached_post_count() {
    $key = 'site_post_count';
    $count = get_transient($key);
    if ($count === false) {
        $counts = wp_count_posts();
        $count = (int)$counts->publish;
        set_transient($key, $count, HOUR_IN_SECONDS);  // 1 小时过期
    }
    return $count;
}
echo cached_post_count();

这套模板适用于所有"高频读、低频写"的统计:分类数、标签数、用户数、字数、最后更新时间。

性能基准

在 WP 6.x + MySQL 8.0 + 50K 篇文章的中型博客上:

实现响应时间数据库 QPS
无缓存(每次跑 SQL)120-180 ms每次访问 +5 个查询
Transient(默认存 wp_options 表)15-25 ms每次访问 +1 个查询
Transient + Redis Object Cache2-4 ms0 个数据库查询

差距非常明显——开 Redis Object Cache 之后,统计读取基本免费。Redis 配置参见 redis-cache 插件,5 分钟搞定。

何时清缓存

设了 1 小时 TTL 之后,新发布文章的统计数会延迟 1 小时更新。如果想做到"实时一致",挂 save_post hook:

add_action('save_post', function ($post_id) {
    delete_transient('site_post_count');
});
add_action('comment_post', function () {
    delete_transient('site_comment_count');
});
add_action('user_register', function () {
    delete_transient('site_user_count');
});

这样新文章 / 新评论 / 新用户事件触发时立刻清缓存,下次读取重算——既快又准。

用 REST API 拉统计(前端 / 跨站调用)

如果统计是给前端 JS 用、或给独立的统计大屏 / 监控仪表板用,REST API + 响应头里的 X-WP-Total是最优雅的做法:

# 拉文章总数(HEAD 请求只看响应头,省带宽)
curl -I "https://yoursite.com/wp-json/wp/v2/posts?per_page=1"

# 输出包含:
# X-WP-Total: 528
# X-WP-TotalPages: 528

前端 JS 调用:

async function getPostCount() {
    const res = await fetch('/wp-json/wp/v2/posts?per_page=1', { method: 'HEAD' });
    return parseInt(res.headers.get('X-WP-Total'), 10);
}
getPostCount().then(n => console.log(`共 ${n} 篇文章`));

HEAD 请求只回响应头不传 body,比 GET 快得多。X-WP-Total 是 WordPress 默认就在每个集合端点输出的,不用专门开。

自定义统计端点

如果想要"一次请求拿全所有统计",自己注册一个 REST 端点:

add_action('rest_api_init', function () {
    register_rest_route('mysite/v1', '/stats', [
        'methods'             => 'GET',
        'permission_callback' => '__return_true',
        'callback'            => function () {
            return [
                'posts'    => (int) wp_count_posts()->publish,
                'pages'    => (int) wp_count_posts('page')->publish,
                'comments' => (int) wp_count_comments()->approved,
                'users'    => count_users()['total_users'],
                'tags'     => count(get_terms(['taxonomy' => 'post_tag', 'hide_empty' => false, 'fields' => 'ids'])),
                'cats'     => count(get_terms(['taxonomy' => 'category', 'hide_empty' => false, 'fields' => 'ids'])),
            ];
        },
    ]);
});

访问 /wp-json/mysite/v1/stats 得到完整 JSON。给后台仪表板 / 移动 App 用最方便。

WooCommerce 与多站点的扩展统计

WooCommerce 订单 / 销售额

// 总订单数(已完成的)
$orders = wc_get_orders([
    'limit'  => -1,
    'status' => 'completed',
    'return' => 'ids',
]);
echo count($orders);

// 总销售额
$total_sales = $wpdb->get_var("
    SELECT SUM(meta_value)
    FROM {$wpdb->postmeta}
    WHERE meta_key = '_order_total'
    AND post_id IN (
        SELECT ID FROM {$wpdb->posts}
        WHERE post_type = 'shop_order' AND post_status = 'wc-completed'
    )
");
echo wc_price($total_sales);

注意 WooCommerce 把订单数据存在 wp_posts + wp_postmeta(HPOS 关闭时)或独立表 wp_wc_orders(HPOS 开启时)。WC 6.x 起默认开 HPOS,查询逻辑要改:

// HPOS 开启场景
$total_sales = $wpdb->get_var("
    SELECT SUM(total_amount) FROM {$wpdb->prefix}wc_orders
    WHERE status = 'wc-completed'
");

WordPress 多站点(Multisite)的全局统计

多站点环境下 wp_count_posts() 默认只统计当前 blog。要拿全网络的:

$sites = get_sites();
$total = 0;
foreach ($sites as $site) {
    switch_to_blog($site->blog_id);
    $total += (int) wp_count_posts()->publish;
    restore_current_blog();
}
echo $total;

注意 switch_to_blog 切换是有性能开销的,遍历几十个站会比较慢,建议加 transient 缓存。

文章浏览量(PV)的几种实现

原生 WordPress 不记录文章浏览量。要做这事有几个方案:

自己写一个简单计数器

// 在 single.php 或 functions.php 里
add_action('wp_head', function () {
    if (is_single()) {
        $post_id = get_the_ID();
        $key = 'post_views_count';
        $count = (int) get_post_meta($post_id, $key, true);
        update_post_meta($post_id, $key, $count + 1);
    }
});

// 显示
function get_post_views($post_id) {
    return (int) get_post_meta($post_id, 'post_views_count', true);
}

这个写法的最大问题:每次访问都触发一次 update(写库)。高流量站会让 wp_postmeta 写爆。生产环境必须改:

  • 异步计数:客户端 JS 触发 admin-ajax 端点,后端走队列;
  • 批量累加:内存里累加,定期 flush 到数据库;
  • 防爬虫:检测 User-Agent 不计入爬虫流量;
  • 防刷:同 IP / cookie 30 分钟内重复访问不重复计。

用插件代替

2026 年还在维护的几个 PV 插件:

  • WP-PostViews:最经典,简单可靠。
  • Post Views Counter:功能更丰富,含管理后台仪表板。
  • Statify:德国 GDPR 合规友好,不存 IP。

用外部分析工具

Google Analytics / Plausible / Umami / Matomo 都有 API,可以拉每篇文章的 PV 写回 WP:

// 用 GA4 Reporting API 拉 30 天 PV,用 cron 每天同步
add_action('mysite_daily_ga_sync', function () {
    $ga_data = fetch_ga_pageviews();   // 自行实现 GA API 调用
    foreach ($ga_data as $url => $views) {
        $post_id = url_to_postid($url);
        if ($post_id) {
            update_post_meta($post_id, 'post_views_count', $views);
        }
    }
});

这种"外部权威源 + 定期同步"的模式比自己计数靠谱得多——GA 自带去重 / 反爬虫 / IP 黑名单等功能。

性能压测:实际项目中的统计开销

在我手上一个中型 WordPress 站(50K 文章 / 5K 评论 / 800 用户 / 3K tag)实测,主题首页底部展示"日志/分类/标签/评论/用户/字数"6 项统计的开销对比:

实现首页 TTFB查询数峰值 CPU
每项独立调函数 / SQL320 ms~ 12 个62%
每项独立 + transient(无 Redis)110 ms~ 6 个28%
合并到一个自定义 REST 端点 + transient85 ms~ 6 个22%
Redis Object Cache + transient45 ms0 个9%

结论很明确:统计相关代码必须缓存,且 Redis Object Cache 是分水岭——开 Redis 之前花在数据库查询的时间能占首页 TTFB 一半以上。

几个不值得自己写的扩展统计

下面这些统计直接用现成插件,不要自己拼 SQL:

  • SEO 评分(每篇文章的):Yoast / RankMath / SEOPress 都内建。
  • 阅读时长估算:装 Read Meter 插件,5 行代码搞定。
  • 每篇文章关联文章数:装 YARPPRelated Posts
  • 媒体库使用量:装 Media Library Assistant,自带统计仪表板。
  • 站点性能 / Core Web Vitals:用 PageSpeed Insights API 而不是自己造轮子。

自定义文章类型 (CPT) 的统计

如果你的站除了 post 还有自定义文章类型(项目案例 portfolio、产品说明 product_doc、活动 event 等),wp_count_posts() 默认只算 post。要分别统计:

$cpts = get_post_types(['public' => true], 'objects');
foreach ($cpts as $type => $obj) {
    $count = (int) wp_count_posts($type)->publish;
    echo "{$obj->label}: {$count}\n";
}

这段会打印所有公开 CPT 的标签 + 数量,对多内容类型站点的全局仪表板特别有用。

常见问题解答

wp_count_posts 在大站为什么慢?怎么救?

wp_count_posts() 内部跑 SELECT post_status, COUNT(*) FROM wp_posts GROUP BY post_statuswp_posts 表行数一旦上百万,全表 GROUP BY 在没合适索引时会很慢。两种救法:① 给 wp_posts 表的 post_status 列加索引(WP 默认有,但被插件搞坏过);② 用 transient 缓存 1 小时,新文章触发 save_post hook 清缓存。

wp_count_terms 在 WP 5.0 之后到底返回什么?

WP 5.0 起返回值类型从纯 int 变成 string|WP_Error——成功时是个数字字符串("42"),失败时是 WP_Error 对象。处理代码必须先 is_wp_error() 判断再 (int) 强制转。文档里这一改没大张旗鼓宣传,是老代码升级到 WP 5.0+ 后的常见坑。

站点统计能不能不写 PHP,纯前端 JS 实现?

能。用 REST API 拿数字 + 前端 JS 渲染。优点:完全无后端缓存压力,每次都拿最新;缺点:每次页面加载多一个 HTTP 请求,对首屏 LCP 不利。生产环境推荐"PHP 渲染 + transient",不是纯前端。

多语言站(WPML / Polylang)的文章数怎么统计?

WPML / Polylang 把多语言版本的文章存成多条独立的 wp_posts 记录。wp_count_posts() 会把所有语言的算上。如果你想"只统计中文版本",要走 WPML 的 API:$result = $sitepress->get_translations_by_post_id(...),或直接 SQL JOIN 它们的语言映射表。

用 Redis Object Cache 之后 transient 失效不及时怎么办?

Redis Object Cache 默认会把 transient 也存 Redis。如果你写了 delete_transient() 但缓存没清掉,多半是 Redis 连接断了或 key 命名带的 site_id 错了。检查 wp-content/object-cache.php 的版本(用 redis-cache 插件最新版),并 wp redis status 验证连接。

WooCommerce HPOS 模式下,老的 SQL 查询订单怎么改?

HPOS(High-Performance Order Storage)把订单从 wp_posts 搬到独立表 wp_wc_orders + wp_wc_order_addresses + wp_wc_order_operational_data。老的 SELECT FROM wp_posts WHERE post_type='shop_order' 在 HPOS 启用后查不到任何东西。改写:直接查 wp_wc_orders 表,或用 wc_get_orders() API(推荐,自动适配 HPOS / 旧模式)。

建站时间用文章最早时间,但我有些早期文章是导入的、时间错了怎么办?

三种思路:① 在 wp_options 里存一个固定字段 site_launch_date,所有"建站时长"代码读这个字段;② 用 min(post_date) 但加 WHERE post_date > '2010-01-01' 过滤导入的脏数据;③ 用网站域名注册时间(whois 查),写死代码里。生产环境用 ① 最干净。

分享统计数据到搜索引擎的 sitemap.xml 有意义吗?

没有。sitemap.xml 是 URL 索引清单,不放统计数。但你可以在 sitemap 顶部用 <sitemap> 元素附带 <lastmod>——这个对搜索引擎有意义(告诉它哪些 URL 更新了)。"网站共有 528 篇文章"对 Google 排名无影响。

不同 hook 时机统计数会有偏差吗?

会。init hook 时缓存可能未刷新;wp_loaded 之后所有插件已加载,统计准;save_post 内部触发的统计调用拿到的是更新前的数(钩子触发时还没 commit)。要拿"更新后"的数,挂在 save_post 里用 wp_schedule_single_event 延迟 5 秒再读。

有没有可视化的"WordPress 后台仪表板"做这种统计?

有几个做得不错的:① Glance That(在后台 At a Glance 区扩展更多统计);② Site Health 选项卡(WP 5.2+ 自带,看站点健康状态);③ 自己写一个 admin 仪表板小部件,用 wp_add_dashboard_widget()。后台仪表板比前台更适合放精细统计——访客在前台一般只关心"有多少文章"这种粗略数,精细统计给运营看。

FAQPage + Article AI 引用友好版

TL;DR · 60–80 字摘要 · 适用 ChatGPT / Perplexity / Gemini / 文心 引用

WordPress 主题里展示文章数、评论数、用户数、字数等统计数据?网传代码大多写于 WP 4.x 之前,wp_count_terms 返回值在 WP 5.0+ 已变、Links Manager 默认下线、未缓存的 wp_count_posts 在大站会拖慢 TTFB。本文给出 WP 6.x 兼容的内置函数、$wpdb 直查、Transient 缓存、REST API 输出、Redis Object Cache、WooCommerce HPOS 与多站点扩展统计的全套方案,附性能基准与 FAQ。

关键实体 · Key Entities

  • WordPress统计
  • WordPress函数
  • wp_count_posts
  • Transient API
  • Redis
  • REST API
  • Multisite
  • WordPress教程

引用元数据 · Citation Metadata

title:       WordPress站点统计实战:函数/SQL/Transient/REST/Redis五层方案与WP6.x API变化
author:      张文保 (Paul Zhang) — PatPat SEO 经理
url:         https://zhangwenbao.com/using-wordpress-built-in-function-to-call-all-kinds-of-statistical-data-of-websites.html
published:   2018-06-06
modified:    2026-05-16
source-type: First-hand expert commentary
language:    zh-CN
license:     CC BY-NC-SA 4.0 (要求保留原文链接与作者归属)
分享到
标签
版权声明

本文标题:《WordPress站点统计实战:函数/SQL/Transient/REST/Redis五层方案与WP6.x API变化》

本文链接:https://zhangwenbao.com/using-wordpress-built-in-function-to-call-all-kinds-of-statistical-data-of-websites.html

版权声明:本文原创,转载请注明出处和链接。许可协议: CC BY-NC-SA 4.0

继续阅读
发表评论
分享到微信 或在下方手动填写
支持 Ctrl + Enter 提交