如何优化 Drupal 的渲染缓存,以提升网站运行性能

成都长风云Drupal开发团队在长期的实践中发现,Drupal 网站运行卡顿的诱因有很多:图片未做优化、冗余模块过多、服务器主机配置较差。但有一个最容易被忽视、同时也最容易解决的问题 —— 渲染缓存配置错误,或是完全没有启用。

长风云信息技术股份有限公司从事18年的Drupal开发、维护工作。在实践中,每当访客访问页面时,Drupal 都会从零开始构建页面:查询数据库、处理视图、拼接区块内容。而渲染缓存能够终止这套重复流程。页面组件渲染完成后,Drupal 会保存生成的页面代码并重复调用;只有在对应内容发生改动时,才会重新生成页面。

成都长风云Drupal开发团队将带大家从代码底层拆解渲染缓存的运行原理、管控缓存的元数据,讲解如何在自定义区块中实现缓存逻辑,以及线上生产环境缓存失效时的排查要点。

一、核心要点

  • 渲染缓存会存储 Drupal 各类组件生成的 HTML 代码,避免每次页面请求都重复渲染构建
  • 缓存元数据(缓存键、缓存标签、缓存上下文、最大缓存时效)决定了哪些内容会被缓存、缓存何时自动清空
  • 缓存元数据书写不规范、缺失不仅会造成渲染缓存失效,还会连带破坏动态页面缓存与内置页面缓存
  • 精准使用缓存标签(例如 node:5),不要一刀切清空全站缓存
  • 高并发访问站点可搭配 Redis、Memcache 等缓存后端协同渲染缓存,实现性能最大化提升

二、Drupal 渲染缓存到底有什么作用?

当 Drupal 组装页面时,会生成渲染数组 —— 这是一套结构化指令,用来描述页面每个组件该如何展示。渲染缓存会提取渲染数组最终生成的 HTML 代码并保存。下次有相同组件访问请求时,Drupal 会跳过重复构建流程,直接返回已缓存好的页面内容。
每个渲染数组都可以携带缓存元数据,用来告知 Drupal 该如何处理缓存:

'#cache' => [
 'keys' => ['my_custom_block'],
 'contexts' => ['url.path', 'user.roles'],
 'tags' => ['node:1', 'taxonomy_term:3'],
 'max-age' => Cache::PERMANENT,
],


下面逐一说明每项配置的作用:

  • keys(缓存键):缓存条目唯一标识
  • contexts(缓存上下文):区分差异化内容;不同角色的用户会各自生成独立缓存副本
  • tags(缓存标签):将缓存条目绑定至指定内容;例如修改编号 1 的文章节点时,对应缓存会自动清空
  • max-age 设置为 Cache::PERMANENT(永久缓存):不会按固定时效自动过期,仅当绑定的缓存标签失效时才会清除

二、缓存元数据缺失或书写不规范为何会引发故障?

如果缺少缓存元数据,Drupal 会出现两种极端问题:要么缓存范围过大,持续展示过期内容;要么直接放弃缓存机制,每次访问都重新渲染页面。两种情况都会造成站点异常,只是故障表现不同。

很多开发团队容易忽略连锁负面影响:动态页面缓存与内置页面缓存在判断页面返回内容时,都会读取渲染缓存元数据。一旦元数据残缺、定义不规范,这两套页面缓存体系也会同步失效,单一缓存问题会衍生三重性能故障。

真实业务流量下,性能损耗会持续叠加:数据库查询次数暴增、服务器 CPU 占用走高、页面加载变慢,而这一切仅仅是因为页面内容并未发生任何修改。

三、如何在 Drupal 自定义区块中实现渲染缓存?

下面提供一套可直接落地的实操示例:自定义区块用于展示最新发布文章,并完整配置规范的缓存元数据。

步骤 1:创建区块插件

文件路径:modules/custom/my_custom_module/src/Plugin/Block/LatestArticleBlock.php
 

<?php
namespace Drupal\my_custom_module\Plugin\Block;
use Drupal\Core\Block\BlockBase;
use Drupal\node\Entity\Node;
use Drupal\Core\Cache\Cache;
/**
* 提供「最新文章」区块
*
* @Block(
*   id = "latest_article_block",
*   admin_label = @Translation("Latest Article Block")
* )
*/
class LatestArticleBlock extends BlockBase {
 public function build() {
   $query = \Drupal::entityQuery('node')
     ->condition('type', 'article')
     ->condition('status', 1)
     ->sort('created', 'DESC')
     ->range(0, 1);
   $nids = $query->execute();
   $output = [
     '#markup' => $this->t('暂无可用文章。'),
   ];
   if (!empty($nids)) {
     $node = Node::load(reset($nids));
     $output = [
       '#markup' => $this->t('<a href=":url">@title</a>', [
         ':url' => $node->toUrl()->toString(),
         '@title' => $node->getTitle(),
       ]),
       '#cache' => [
         'keys' => ['latest_article_block'],
         'tags' => $node->getCacheTags(),
         'contexts' => ['url.path'],
         'max-age' => Cache::PERMANENT,
       ],
     ];
   }
   return $output;
 }
}


步骤 2:部署区块

执行命令 drush cr 清空缓存,随后进入后台:结构 > 区块布局 > 放置区块,选择「Latest Article Block」。
页面首次加载完成后,Drupal 会缓存渲染结果。当文章更新或发布新文章时,缓存标签会自动完成失效清理,仅清空该区块缓存,不会影响网站其余页面。

四、上线部署后常见缓存故障,以及缓存有效性校验方法

本地开发环境配置好规范元数据只是第一步,线上生产环境缓存相关 Bug 十分常见,且故障不会主动告警提示。

需要重点排查的缓存失效场景

上线后最高发的问题:内容更新后缓存未自动清理,根源通常分为三类:

自定义模块仅在内容保存钩子执行 Cache::invalidateTags() 清理缓存,缺失删除、取消发布钩子;删除文章节点后,旧缓存数据依旧残留。

反向代理服务(Varnish、Nginx)未兼容 Drupal 的 Cache-Control、Surrogate-Key 响应头,基于标签的缓存清理指令无法同步至代理层。

第三方贡献模块渲染内容时,未将缓存标签向上传递至渲染树,Drupal 无法识别该区块依赖对应内容,缓存不会自动刷新。
若站点使用 Redis 或 Memcache 作为缓存后端,还需确认程序是否静默降级至 PHP 文件缓存驱动:缓存服务连接中断时 Drupal 会自动切换数据库存储缓存,功能看似正常,但高并发下性能差距极大。

上线后校验缓存是否正常生效

快速简易校验:浏览器无痕模式打开页面,调出开发者工具网络面板,查看响应头 X-Drupal-Cache。重复刷新页面若显示 MISS,代表缓存未正常存储或读取。精准代码调试:借助 drush 执行 PHP 代码,单独查询指定缓存容器:

$cache = \Drupal::cache('render')->get('my_cache_key');
var_dump($cache);

可视化日志排查:在 settings.local.php 开启配置 $settings['cache.debug_backtrace'] = TRUE;,查看系统日志追踪组件绑定的缓存标签;也可安装 Cache Debug 模块,页面悬浮层直接展示全部缓存信息,可视化排查效率更高。

每次大型版本上线后,务必检查带个性化、按角色区分内容的页面。这类页面最容易因缓存上下文配置错误,出现不同用户读取到他人缓存内容的串号问题。

五、Drupal 渲染缓存最佳实践

以下是 Drupal 渲染缓存开发规范清单:

自定义渲染数组必须完整定义缓存元数据

缺少元数据会让缓存逻辑完全失控:要么缓存过度展示过期内容,要么彻底关闭缓存、重复渲染。该规范适用于自定义区块、自定义渲染组件,以及预处理函数中所有随内容变化的输出。

使用精细化缓存标签,禁止全局清空缓存

node:5、taxonomy_term:10 这类精准标签只会清理关联缓存;虽然 drupal_flush_all_caches() 清空全站缓存、清空整个缓存容器能解决临时问题,但会删除所有有效缓存条目。访问量大的站点会瞬间进入冷缓存状态,全站加载速度暴跌。

谨慎添加 user.roles、user.permissions 缓存上下文

部分场景确实需要使用,但每新增一条上下文,Drupal 就要生成多份差异化缓存副本。若区块添加 user.permissions,系统会为每一组权限组合单独存储缓存。仅当页面输出确实因角色 / 权限产生差异时再配置,不要作为兜底默认配置。

上线前关闭开发环境缓存调试模式

道理浅显,但经常被忽略。本地开发关闭缓存的配置若同步到生产环境,会持续拖垮站点性能,等到数据库查询压力过载才会被发现。

高流量站点搭配 Redis 或 Memcache 作为缓存底层存储

Drupal 默认数据库缓存容器虽能正常运行,但高并发场景扩展性极差。Redis、Memcache 将缓存数据存放于内存,读写速度远高于磁盘数据库。

两者实际差异:

Redis 支持复杂数据结构与持久化存储,服务器重启后缓存数据不会丢失,通用性更强;

Memcache 架构轻量化,单次操作资源开销更低,超大流量、短时效缓存场景吞吐量更优。

注意:二者无法替代渲染缓存,仅作为缓存底层存储层;CSS、JS 静态资源聚合由 AdvAgg 单独处理,和渲染缓存相互独立。

六、总结

缓存元数据是渲染缓存正常运转的核心。弄懂缓存键、缓存标签、缓存上下文三者各自的管控逻辑,整套缓存体系就很容易理解。Drupal 绝大多数缓存故障,根源都是自定义代码没有统一规范编写元数据。只要规范落地,大量站点性能问题会自行消失。

如果你需要全站缓存审计,或是协助统一配置站点渲染缓存,Specbee 的 Drupal 技术专家可以提供专业支持。

七、常见问题

1. 什么是 Drupal 渲染缓存?

渲染缓存会存储 Drupal 渲染数组生成的 HTML 代码,避免每次页面请求重复构建组件。依靠缓存键、缓存标签、缓存上下文、最大缓存时效这套元数据,系统判断缓存内容是否有效、何时重新生成页面。

2. 渲染缓存和动态页面缓存、内置页面缓存有什么区别?

三者运行层级完全不同:
渲染缓存:组件级缓存,作用于独立区块、视图、页面模块;
动态页面缓存:登录用户完整页面缓存;
内置页面缓存:匿名访客完整页面缓存。
三者存在依赖关系:动态页面缓存与内置页面缓存的运行逻辑,全部依赖渲染缓存元数据。

3. 渲染数组不添加缓存元数据会有什么后果?

会出现两类严重问题:一是缓存范围过大,长期展示过期内容;二是直接禁用缓存,每次访问都完整重建页面。同时动态页面缓存、内置页面缓存会因元数据残缺同步失效。

4. max-age 什么时候配置为 Cache::PERMANENT 永久缓存?

当缓存条目仅需在关联内容修改时清理、不需要按时间自动过期时使用。搭配精准缓存标签,内容更新后缓存会自动失效刷新。

5. 该选用哪些缓存上下文?

由页面差异化维度决定:url.path 按页面路径区分缓存;user.roles 按用户角色区分缓存。仅添加业务真正需要的上下文,每多一条上下文,缓存副本数量会成倍增加。

6. 启用渲染缓存必须安装 Redis 或 Memcache 吗?

不需要。Drupal 自带数据库缓存驱动即可完整运行渲染缓存。Redis、Memcache 仅在高流量站点提升读写性能、增强扩展性,不属于渲染缓存运行的必备组件。

7. 如何排查 Drupal 渲染缓存故障?

开启 settings.local.php 内的缓存调试配置查看日志;
通过 \Drupal::cache()->getMultiple() 代码直接读取缓存条目校验;
安装 Cache Debug 模块,页面悬浮层可视化查看所有组件缓存元数据。

联系我们

提供基于Drupal的门户网站、电子商务网站、移动应用开发及托管服务

长按加微信
长风云微信
长按关注公众号
长风云公众号