我们是如何构建一个在 WordPress 上服务 80K+ 页面且不使用 wp_posts 的程序化 SEO 引擎

发布: (2026年3月24日 GMT+8 19:28)
11 分钟阅读
原文: Dev.to

Source: Dev.to

当我们决定打造 startup‑cost.com 时,就已经知道传统的 WordPress 方案根本不够用。我们需要提供 79 000+ 个独立页面——每个页面对应 479 个城市和 167 种业务类型的组合——并且要展示真实的成本数据、实时计算以及可靠的性能。

大多数人听到 “WordPress 上的 80 K 页面” 就会觉得我们疯了。WordPress 不是博客平台吗?的确如此——但在底层它其实是一个灵活的 PHP 框架,拥有强大的重写引擎。我们只需要抛弃那些无法扩展的部分,自己动手构建即可。

下面就是我们 wp_posts 中没有任何一行记录 的实现过程。

大规模使用 wp_posts 的问题

WordPress 将所有内容存储在名为 wp_posts 的单一表中。对于拥有几百个页面的博客或小型企业站点,这种方式运行良好。但当你向该表中插入数万行数据时,问题会迅速显现:

问题为什么会受影响
查询性能下降WordPress 几乎在每个查询中都要将 wp_postswp_postmeta 进行关联。拥有 80 K 篇文章且每篇文章有 10+ 个元字段时,wp_postmeta 中的记录会超过 800 K 行。原本约 5 ms 的查询现在需要约 500 ms。
后台管理面板变得难以使用加载包含 80 K 条目的 “全部文章” 页面是一场噩梦——虽然有分页,但统计总数的过程会耗费极长时间。
修订历史占用磁盘空间自动保存会在修订记录中生成实际内容的 3‑4 倍数据,尤其是内容通过程序生成时更是如此。
XML 站点地图卡死常用的站点地图插件尝试一次性查询所有文章;当表中有 80 K 行时,它们要么超时,要么耗尽内存。
导入/导出失败WordPress 导出会生成单个 XML 文件。一个包含 80 K 篇文章的 WXR 文件几乎不可能正常处理。

我们需要一种根本不同的解决方案。

为什么不使用静态站点生成器或其他 CMS?

Fair question. We evaluated Hugo, Next.js, and a custom Node.js app. WordPress still gave us specific advantages:

  • 托管费用极低 – 共享的 WordPress 主机每月只需几美元,且能够轻松应对我们的流量。
  • 插件生态系统 – 我们仍然使用实用插件来进行 SEO、缓存以及其他任务。
  • 熟悉的部署方式 – 我们的团队对 WordPress 了如指掌。无需学习曲线,也不需要新的 CI/CD 流水线。
  • PHP 已足够快 – 在使用 OpCache 和清晰的查询模式后,PHP 8 能在两位数毫秒内响应页面。

The key insight: we don’t have to use WordPress the way it was designed. We can treat it as a routing and rendering framework while storing our data however we want.

架构 – 高层概览

我们构建了一个自定义的 WordPress 插件,完全绕过 wp_posts。该概念基于三大支柱:

自定义数据库表

我们没有把所有数据塞进 postspostmeta,而是创建了专用表,拥有合适的模式、数据类型和索引。可以把它看作是运行在 WordPress MySQL 实例中的一个小型应用数据库。

  • 每个实体对应一张表(城市、业务类型、成本指标)。
  • 列与实际数据模型相匹配——不使用通用的键值对。
  • 采用恰当的索引、规范化,以及只检索所需数据的查询。

结果:对 80 K 条帖子的基于 meta 的查找可能需要数百毫秒;而对专门构建的表进行直接索引查询则能在个位数毫秒内返回。

虚拟 URL 路由

WordPress 的 rewrite API 功能强大,却常被低估。大多数开发者只通过永久链接设置界面稍作触碰。实际上,你可以在底层注册完全自定义的 URL 模式,并将其映射到自己的渲染逻辑。

  • 定义 WordPress 能识别的 URL 模式。
  • 当请求到达时,WordPress 匹配 URL、提取 slug,并将控制权交给我们的插件。
  • 不会创建帖子,也不会触碰 wp_posts 中的任何行——该 URL 之所以存在,仅因为我们让 WordPress 认识了这个模式。

这样既能完全掌控 URL 结构,又能受益于 WordPress 的请求生命周期、缓存钩子以及插件兼容性。

动态内容生成

程序化 SEO 只有在每个页面提供真实价值时才有效。我们并非仅在模板中替换城市名称;每个页面都会展示 真实的成本数据、真实的计算结果以及真实的对比

  • 渲染层从我们的自定义表中提取数据。
  • 对每个城市‑业务组合执行特定计算。
  • 输出完整的 HTML,包含唯一的标题、meta 描述、结构化数据(schema)标记等。

Google 能识别薄弱、模板化的内容。每页都具备数据驱动的真实价值,才是程序化 SEO 能长期奏效的关键。

性能结果

基准测试在相同的服务器上运行(共享主机,PHP 8.1,MariaDB 10.6)。

指标wp_posts 方法自定义方法
平均页面加载时间(TTFB)~800 毫秒~120 毫秒
数据库查询时间~200 毫秒(元数据联接)~15 毫秒(索引查找)
后台仪表盘加载30+ 秒即时(无后台开销)
站点地图生成超时(60 秒)~2 秒
每个请求的内存使用量~64 MB~12 MB

差异显著。使用适当索引的自定义表使 WordPress 的性能如同专门构建的应用程序。

网站地图策略

Google 的限制:每个文件 50 000 个 URL未压缩大小不超过 50 MB。对于 80 K 页面,我们需要多个站点地图以及一个索引。

  • 以编程方式生成站点地图,将 URL 切分为可管理的文件。
  • 创建一个站点地图索引,引用所有切片。
  • 在每周的 cron 任务中运行生成过程。
  • Google Search Console 能够顺利抓取这些文件,我们还能按站点地图切片跟踪索引进度。

内部链接

有 80 K 页面,内部链接对 SEO 和帮助用户导航都至关重要:

  • 每个城市页面链接到该城市中所有可用的业务类型。
  • 每个业务页面链接到该业务类型的热门城市。
  • 每个详情页面根据地理和主题的接近性链接到相关页面。
  • 密集的内部链接图有助于搜索引擎发现并理解站点结构。

缓存

  • 并非所有 80 K 页面都拥有相同的流量。
    • 热门的城市/业务组合会预缓存,并使用更长的 TTL。
    • 长尾页面按需缓存,使用较短的 TTL。
  • 我们还在数据库查询层面缓存经常访问的聚合。

我们学到了什么

  • WordPress 能够处理大规模 – 但前提是你要跳出其默认的内容模型。该平台比人们想象的更灵活。
  • 自定义表不是“投机取巧” – 当你的数据不符合文章/元数据模式时,它们是正确的工具。WordPress 本身就为评论、用户和选项使用自定义表。
  • 虚拟路由功能强大 – WordPress 的重写规则可以处理复杂的 URL 模式。仅仅因为你的 URL 与文章不对应,并不意味着需要自定义框架。
  • 程序化 SEO 需要真实价值 – Google 能识别薄弱、模板化的内容。每个页面都必须拥有真正不同且有用的数据。这就是垃圾信息与真实产品的区别。
  • 从数据模型开始 – 我们花在设计数据库模式上的时间比写渲染代码更多。良好的数据建模在各层面都能带来回报。
  • 先监控,再优化 – 我们最初没有使用缓存,只在监控显示瓶颈的地方才添加。过早的优化会带来不必要的复杂性。

查看结果:startup-cost.com – 为全球 479 个城市的 167 种业务类型提供创业成本估算。

由 Kavela Ltd 构建 – 我们在规模化下构建数据驱动的网络工具。

0 浏览
Back to Blog

相关文章

阅读更多 »