“Drupal 很慢”这个名声,绝大部分来自主机托管,而它几乎总是一个采购决定,而不是软件问题。站点被认真地做了出来,然后上线在一个按“几个 PHP 文件的宣传册站点”定价的套餐上。结果就是:一套带着正经渲染管线的内容管理系统,跑在一个自己改不了的内存上限里,跑在一个自己控制不了的操作码缓存上,还没有一个能运行自身工具的命令行。
人们心里拿来对比的是 WordPress,而这个对比是错的。WordPress 几乎在任何环境下都能凑合跑,是因为它的市场份额逼着主机商把它做成几乎在任何环境下都能凑合跑。Drupal 的前提不同:它假定你有较新的 PHP、较新的数据库、真正的缓存后端、一个命令行,以及一套把代码库当成构建产物而不是当成“可以直接编辑的文件夹”的部署流程。
Drupal 到底需要主机提供什么? 不低于你所用版本下限的 PHP、较新的 MySQL、MariaDB 或 PostgreSQL、实践中 256MB 的 PHP 内存、OPcache、能运行 Composer 与 Drush 的命令行访问、一条真正的 cron 计划任务,以及在有登录用户之后的外部对象缓存。廉价的共享主机会同时在其中三四项上不合格。
Drupal 对服务器的真实要求
官方公布的要求很短、很具体,而且完全公开,可几乎没有人在付款之前读过它。这些要求还是分版本的,而这一点眼下格外重要,因为其中两条下限会在 2026 年 12 月发生变化。
PHP 版本下限没有商量余地
Drupal 11 要求 PHP 8.3 起步,支持 8.3、8.4 和 8.5。Drupal 10 要求 8.1,最高支持到 8.4,而 Drupal 12 又把下限抬到了 PHP 8.5。这些数字来自 Drupal 自己的 PHP 要求文档,也只有这一份清单值得信任。
这件事比看上去更重要,因为 PHP 分支本身也会到期。php.net 的支持版本页面 把 PHP 8.2 的安全支持终止日定在 2026 年 12 月 31 日,8.3 到 2027 年 12 月 31 日,8.4 到 2028 年 12 月 31 日。PHP 8.1 已经过期了。一家宣传“提供 PHP 8.1 和 8.2”的主机商,卖给你的是一套现在就没有补丁、或者几个月内就会没有补丁的技术栈。
两个日期还撞在了一起。Drupal 10 在 2026 年 12 月 9 日结束生命周期,而根据 核心发布计划,Drupal 12 就在同一周发布。如果你的主机提供不了 PHP 8.3 或更高版本,那之后你就无法运行受支持的 Drupal。如果你还停在 10,我们关于 Drupal 迁移的费用、路径与期限 的文章讲了这意味着什么。
数据库引擎和它们真正的最低版本
按照 数据库服务器要求,Drupal 11 需要 MariaDB 10.6 及以上、MySQL 8.0 及以上、PostgreSQL 16 及以上,或者 SQLite 3.45 及以上。Drupal 10 宽松得多,只要 MariaDB 10.3.7、MySQL 5.7.8、PostgreSQL 12 和 SQLite 3.26。这正是为什么一次升级有时会连带逼出一次客户完全没预料到的数据库升级。
有两个细节常被跳过。在 MySQL 和 MariaDB 上,存储引擎必须是 InnoDB,因为 Drupal 依赖事务和行级锁。在 PostgreSQL 上,安装之前必须在 Drupal 使用的数据库里创建 pg_trgm 扩展,而一个不允许你执行 CREATE EXTENSION 的托管数据库服务就是不可用的。
SQLite 确实受官方支持,用于本地开发也完全够用,但一旦有好几个编辑同时保存内容,它就不是生产环境的答案了,因为最先触顶的是写入并发。
内存、扩展与 Web 服务器
Drupal 文档里的最低值是 64MB 的 PHP 内存,低于这个数它会给出警告。同一页还写着,生产环境上 128MB 或 256MB 是常态,媒体文件多的站点需要更多。实践中请把 256MB 当作工作值,并且预期还要为命令行再调高一些,因为吃内存的是 Composer 和大规模迁移,而不是页面响应。
扩展清单平淡无奇,但仍然值得逐项核对:带数据库驱动的 PDO、XML、JSON、mbstring、cURL、用于对外 HTTPS 的 OpenSSL,以及生成图片衍生尺寸所需的 GD 或 ImageMagick。Drupal 12 还加上了用于密码哈希的 Argon2,这又是一件很旧的 PHP 构建不会带的东西。
在 Web 服务器一侧,Drupal 支持 Apache 2.4.7 及以上和 Nginx 1.1 及以上,并且 从 Drupal 11.0.0 起不再支持 Microsoft IIS。Apache 需要 mod_rewrite 来提供干净的 URL,还需要 AllowOverride All 才能让随包发布的 .htaccess 生效。最后这一点最容易绊倒人:Drupal 的一部分保护规则只存在于 .htaccess 里,所以用 Nginx 部署时必须在服务器配置中手工把它们重写一遍。这是一个例行步骤,也是一个被例行跳过的步骤,而它正是我们做 服务器安全审计 时最先要看的东西之一。
共享主机为什么撑不住 Drupal
共享主机并不是劣质主机,它只是为另一种形状的应用做了优化,而 Drupal 会在它的约束上以四种可预测的方式撞碎。
没有命令行就没有 Composer 和 Drush
现在的 Drupal 是一个 Composer 项目。核心、贡献模块以及它们的 PHP 依赖全部由 Composer 解析,而一旦 Composer 开始管理某个模块,它就必须连核心一起管理。把 Composer 和手工改文件混着用,正是一个站点最后完全无法更新的典型路径。
一个带文件管理器的控制面板做不到这件事,FTP 客户端也做不到。没有 SSH,你同时也失去了 Drush,而重建缓存、导入配置、执行数据库更新和重置用户密码,实际上都是靠 Drush 完成的。一个你无法对它执行 drush cr 的站点,是一个每一步恢复动作都要变成工单的站点。
那些你看不见、更改不了的限制
共享账户上的 memory_limit 由别人决定,通常是 128MB,有时更低,而且当你遇到那次需要 512MB 的迁移时,没有任何途径把它调上去。
更大的问题是 OPcache。它把预编译的脚本字节码放在共享内存里,这样 PHP 就不必在每个请求上重新解析文件;而在共享主机上,那块内存池是几百个账户共用的。Drupal 有好几千个 PHP 文件,所以它在这块被争抢的内存池里是个重量级租客,于是被挤出去。症状就是:有人访问之后的一分钟里站点很快,一个小时后又变慢了。
然后是那些干脆就不存在的东西:没有 Redis,没有 Memcached,没有对 PHP 进程管理器的控制权,也没有办法跑一个长时间运行的队列工作进程。
从来没有真正跑起来的 cron
Drupal 的自动 cron 模块 默认每三小时执行一次,并且由访问站点的最终用户触发。在流量大的站点上,这意味着偶尔会有某个访客用自己的页面加载时间替搜索索引买单。在冷清的站点上,这意味着 cron 实质上根本不跑:搜索索引会过期,日志表永远不会被清理,可用的安全更新也永远不会被检查。
文档建议改为从外部触发 cron,因为那样总能按时运行,而且消耗的资源更少。这需要一条真正的 crontab 记录,而最便宜的那一档并不提供。
各层缓存,以及访客真正命中的是哪一层
Drupal 的缓存层比多数人以为的要多,而且这些层次不是互相替代的关系。它们是叠起来的,每一层接住上一层没能接住的东西。
OPcache 完全位于 Drupal 之下
OPcache 不是 Drupal 的功能。它在解释器这一层缓存编译后的 PHP 字节码,所以无论 Drupal 的哪一层缓存是否命中,它对每个请求都生效。这一层配错了,上面的任何东西都补不回来,因为每个请求在到达路由之前,都要先付一遍重新编译框架的代价。共享内存要给得宽裕,并且在生产环境关闭时间戳校验,因为生产环境上的文件集合只在部署时才变化。
Internal Page Cache 与 Dynamic Page Cache
Internal Page Cache 是一个核心模块,默认开启,而且只服务匿名用户。它假定每个匿名访客看到的是同一个页面,在首次请求时把整个响应存下来,之后重复使用。对于没有个性化的营销站点,几乎所有工作都是这一层做的。
Dynamic Page Cache 同样是核心模块,同样默认开启,但它对所有人生效,包括已登录用户。它的做法是让渲染系统把页面上真正属于个人的部分换成占位符,再把周围的一切缓存起来。这就是为什么一个登录状态下的 Drupal 页面依然可以大部分被缓存:真正动态的只有用户菜单和少数几个区块。
BigPipe 与渲染缓存
在这两者之下是渲染缓存,它缓存单个区块、字段、视图结果和实体渲染结果。一个没有命中页面缓存的页面,通常也是靠一堆渲染缓存命中拼起来的,而不是从数据库重新构建。
剩下的部分交给 BigPipe。它从 Drupal 8.1 起进入核心,在 8.3 转为稳定,从 8.5 起进入标准安装配置。它不再等所有占位符解析完毕,而是立刻把可缓存的页面刷出去,随后再把个性化片段流式送达。它不需要任何配置,而且对已登录用户的帮助远大于对匿名用户。
所以,在一个配置正确的站点上,匿名访客命中的是 Internal Page Cache,或者它前面的 CDN,并且根本碰不到上面这些东西的大部分。登录中的编辑则在每个请求上都命中 Dynamic Page Cache、渲染缓存和 BigPipe,这正是已认证流量成本高得多的原因。
外部对象缓存
上面每一层都需要一个地方来存放自己的条目。默认那个地方是数据库里的缓存表,也就是说你的缓存读取会在同一台服务器上和内容查询互相抢资源。
Redis 模块 把缓存、锁、洪水控制和队列这几个后端搬到 Redis 或 Valkey 这类兼容存储上,可以用 PhpRedis 扩展、Relay 扩展,也可以用纯 PHP 的 Predis 库。Memcached 是等价的替代方案。对一个小型匿名站点,这改变不了多少。对一个有登录用户的站点,它通常是眼下能拿到的最大一项改进,因为它把最吵闹的写入负载从数据库里搬走了,同时让加锁变得便宜。
Drupal 前面的反向代理与 CDN
Varnish、Nginx 这样的反向代理,或者一个 CDN,会在 PHP 完全没被牵扯进来之前就把请求答掉。对匿名流量来说,这是“用个位数毫秒送出一个页面”和“用几百毫秒送出一个页面”的差别。它同时也是人们最害怕的一层,因为在新闻站或商店上留下过期内容是一种看得见的失败。
让 CDN 变安全的是缓存标签
Drupal 的答案是缓存标签。缓存标签描述的是数据依赖,写成 node:5、user:3 或 node_list 这样的字符串。每一条缓存条目都记录自己依赖哪些标签,所以编辑第 5 号节点时,凡是引用过它的缓存片段、页面和视图,无论出现在哪里都会被作废。
关键在于 Drupal 能把这些标签对外发布出去。贡献模块会把它们作为 Fastly 的 Surrogate-Key 头或 Cloudflare 的 Cache-Tag 头发出去,CDN 随后在 Drupal 发话时按标签清除。这就把 CDN 从一场基于时间的赌博变成了事件驱动的缓存:你可以设置很长的存活时间,因为一次编辑会在几秒内精确清除受影响的那些 URL。
要留意头部预算。Cloudflare 的 按缓存标签清除文档 规定 Cache-Tag 头在去掉字段名之后总长上限为 16KB,大致相当于 1,000 个唯一标签,API 调用中每个标签最长 1,024 个字符,控制台一次清除最多 100 个标签。一个列出大量实体的 Drupal 视图产生的标签会远远超过这个数,所以一个“什么都列”的页面会悄无声息地把头部撑爆,除非在模块里配置了裁剪或哈希。
选择 Drupal 主机:四个档位,说实话
真正的选项只有四个,而选哪一个由两件事决定:你有没有已认证流量,以及你有没有人来运维这台服务器。下面的区间是英国客户按我们的经验通常支付的价格,不含增值税,属于参考值而不是任何供应商的报价。
| 档位 | 每月典型费用 | 买到的是什么 |
|---|---|---|
| 共享主机 | GBP 3 到 GBP 15 | Drupal 需要的一样也没有 |
| 非托管 VPS 或云主机 | GBP 20 到 GBP 120 | 完整控制权,但没有运维的人 |
| 托管型 Drupal 平台 | GBP 40 到 GBP 800 以上 | 一套定好型的技术栈和流程 |
| 定制基础设施 | GBP 400 起 | 什么都有,连同随之而来的义务 |
共享主机
不适合任何要在生产环境跑 Drupal 的人。它在命令行访问、内存、操作码缓存、对象缓存和 cron 上会同时不合格。如果预算确实卡在这里,用差不多的钱买一台小的非托管实例,是更好的花法。
非托管 VPS 或云主机实例
每月大约 GBP 20 到 GBP 120 能买到一台拥有完整 root 权限的机器,这适合绝大多数 Drupal 站点。你可以自己选 PHP 版本、把 OPcache 调到合适大小、装上 Redis、设置 crontab、把 Nginx 配置正确。你买不到的,是有人替你去做这些、去打补丁、去监控、去在凌晨两点把它恢复回来。这一档适合你已经有开发者或代理商在服务合约里的情况,而真正的成本是那份合约,不是那台实例。
托管型 Drupal 专业平台
托管型 Drupal 主机对一个小站点大约从每月 GBP 40 起步,并随流量、环境数量和支持等级迅速上升。你买到的是一套本来就正确的、定好型的技术栈,外加基于 Git 的部署、预发布环境、备份,以及一个真正懂 Drupal 的人接电话。当另一个选项是“没有人”时,它很值;当你为一个每月 4,000 次访问的宣传册站点支付平台价格时,它就不值。
完整的定制基础设施
独立数据库、专用缓存节点、负载均衡后面的一排应用服务器、放文件的对象存储。这套东西大约在每月 GBP 400 以上才开始说得通,而且只在已认证流量、系统集成或者合规要求让托管档位变得别扭的时候才成立。它是能力最强的一档,也是要求最高的一档,因为打补丁、监控和灾难恢复现在都归你了。这些复杂度在应用层是从哪里来的,我们的 Drupal 网站开发指南 有讲。
部署:不要在服务器上编辑文件
既然 Composer 解析的是整棵依赖树,服务器上的代码库就是产物,不是工作区。就地改一个模块文件,意味着下一次 composer update 会把它覆盖掉,也意味着你的生产代码不再对应 Git 里的任何东西。
一套正常的发布流程会在别处构建这个产物:在 CI 里从已提交的 composer.lock 执行 composer install,这样构建是可复现的,而生产主机根本不需要 Composer、不需要为依赖解析准备 PHP 内存,也不需要对 vendor 的写权限。把结果发过去,执行数据库更新,导入配置,重建缓存。回滚就是把指针指回上一个产物。
这也顺带悄悄回答了主机的问题:一家指望你通过 FTP 编辑文件的主机商,无论规格表上写着什么,都和 Drupal 的维护方式不兼容。
配置同步放在什么位置
Drupal 把生效的配置保存在数据库里,并把它导出成 YAML 文件,内容类型、字段、视图和各项设置就是这样在环境之间迁移的。配置管理文档 说明源站点和目标站点的 site UUID 必须一致,这也是第一次导入失败最常见的原因。
实际上,这意味着配置就是代码。它和其它代码一起被提交、被评审、被部署,而导入这一步作为发布的一部分执行,而不是事后在管理界面里点一遍。主机也必须支撑这一点:你需要一个执行导入的地方,还需要一个环境,能让失败的导入被回滚,而不是半途留在生产环境里。
文件、媒体与备份
Drupal 有两套文件系统,而这个区别是承重的。公开的那套位于 Web 根目录之下,由 Web 服务器直接提供。私有的那套位于 Web 根目录之外,每一次对私有文件的请求都会穿过 Drupal,以便检查访问权限。
因此私有文件比公开文件昂贵得多,因为每一次下载都要启动一次 PHP。一个要分发大体积私有文档的站点,需要静态文件服务器根本不需要的余量。
把媒体卸载到对象存储
一旦文件长在应用服务器上,横向扩容和重建都会变得难受,而且每一次备份都要驮上整个媒体库。把公开文件系统迁到 S3 兼容的对象存储,会把两者分开,让 CDN 可以直接提供媒体,也让应用服务器真正变成可丢弃的。
备份证明不了的事,恢复演练能证明
一次备份只能证明文件存在。一次恢复演练能证明这个文件是完整的、数据库和文件来自同一时刻、你的凭据仍然有效,以及整件事要花多长时间。这是四种彼此独立的失败方式,而控制面板里的一个绿色对勾一种都看不出来。
至少每年做两次,用真实的秒表计时,并把那个数字写下来。如果恢复要花六个小时而你的容忍度是一个小时,那么问题在架构,不在备份。恢复时间是一项主机需求,它属于需求说明书,而不属于事故现场。
正确地为 Drupal 主机做容量规划
页面浏览量是错误的单位。一个每月 200,000 次匿名页面浏览、前面挂着 CDN 的站点,在一台小实例上可以很从容;而一个每月 8,000 次浏览的站点,如果其中大部分是登录状态,反而可能吃力。
真正的驱动因素是已认证流量
匿名请求可以由页面缓存或 CDN 回答,完全不碰 PHP;已认证请求不行。它们中的每一个都要跑一遍渲染管线、对每个实体检查访问权限、解析占位符并写入会话数据。实际要问的问题不是你有多少次访问,而是你同时有多少个登录用户,以及这些用户被允许看到什么。
编辑是最极端的情况。内容管理界面属于 Drupal 里最重的页面,而且按定义就无法缓存,所以一个有十二名编辑同时工作的站点,会有一项它的公开流量完全暗示不出来的真实并发需求。
视图、分类法与队列作业
除了已认证流量之外,可靠的成本中心还有几处:带很多过滤器和关联的大型视图会生成昂贵的联表查询;很深的分类法树在每次渲染时都要遍历术语层级;以及搜索索引、订阅源导入和媒体衍生文件生成这类 cron 或队列作业。
如果做得到,队列工作进程不应该和访客抢同一台 Web 服务器的资源。在规模较大的站点上,它们应该放在单独的进程或实例上,这样导入积压就不会拖慢前台。这是一个在采购时就要做出的架构决定,也正是托管档位开始不合身、而定制基础设施开始说得通的那个交界点。
把这个决定做对
这个模式很一致:主机不是买小了,而是买错了形状。没有命令行,没有对象缓存,操作码缓存由别人控制,PHP 版本又快要脱离支持。把同一个站点搬到一台价格相近但规格正确的实例上,通常胜过任何数量的前端优化。
Mecanik 在 网站开发 工作中负责规划、搭建和维护 Drupal 基础设施,当客户担心的是暴露面而不是速度时,我们会通过 服务器安全审计 审视现有技术栈。如果你需要的是人而不是平台,我们关于 招聘 Drupal 开发者 的文章讲了该看哪些方面。
常见问题
Drupal 11 的最低服务器要求是什么? PHP 8.3 或更高版本,数据库为 MariaDB 10.6、MySQL 8.0、PostgreSQL 16 或 SQLite 3.45。Web 服务器一侧是 Apache 2.4.7 或 Nginx 1.1 及以上,并且从 Drupal 11.0.0 起不再支持 Microsoft IIS。PHP 内存低于 64MB 时 Drupal 会发出警告,但生产环境上现实的数字是 256MB。
Drupal 能跑在共享主机上吗? 它能装上,也能返回页面,但共享主机通常会同时在三四项要求上不合格:没有运行 Composer 或 Drush 的命令行访问、无法调高的 PHP 内存上限、没有 Redis 或 Memcached,以及只有当访客恰好打开页面时才触发的 cron。Drupal 慢这个名声,正是这些条件造出来的。
Drupal 主机托管应该花多少钱? 一个小型 Drupal 站点跑在配置正确的非托管实例上,在没有人替你运维之前,通常每月在 GBP 20 到 GBP 120 之间。托管型 Drupal 平台常见的起步价接近 GBP 40,并随流量和环境数量升到几百英镑。定制基础设施大约要在 GBP 400 以上才开始说得通。最便宜的那一档,很少是最便宜的结果。
Drupal 需要 Redis 吗? 对一个小型匿名站点不需要,Internal Page Cache 加数据库就足够了。一旦你有了登录用户、编辑人员或者会员区,Redis 或 Memcached 这样的外部对象缓存就会把缓存读取、加锁和队列从数据库里搬走,而按花的钱来算,这通常是能买到的最大一项改进。
在动态的 Drupal 站点前面放 CDN 安全吗? 安全,前提是按缓存标签作废,而不是按时间。Drupal 会为一个响应所依赖的一切记录诸如 node:5 这样的标签,模块再把它们翻译成 Cache-Tag 或 Surrogate-Key 头,于是 CDN 精确清除一次编辑所影响的那些页面。没有按标签作废,你就只能在过期页面和形同虚设的缓存之间二选一。
评论