Drupal 7 缓存功能入门

在 Drupal 中生成标记时,您通常希望将输出存储在缓存中,而不是每次都重新生成。这对于在页面请求之间不会改变的潜在昂贵的渲染任务尤其重要。Drupal 7 带有一个缓存系统,可以利用cache_get()和cache_set()功能。还有第三个被调用的函数drupal_static()也填补了这两个函数之间的空白。

缓存可用于各种情况,因为它内置于 Drupal 中,您还可以依赖 Drupal 的缓存管理功能。基本的经验法则是永远不要重新生成任何可以缓存和返回的东西。如果生成输出是一项耗时且昂贵的任务,这一点尤其重要。可以轻松缓存的输出的一个很好的例子是每天更新一次的新闻提要。在这种情况下,输出只需要在内容更新时重新生成,并且可以在需要再次重新生成之前保留一段时间。您还可以缓存更复杂的内容,例如搜索结果或搜索页面上的方面,生成这些内容都可能很耗时,但从页面请求到下一个页面请求不会发生太大变化。

Drupal 中的静态缓存

该drupal_static()函数通过创建一个全局缓存来工作,该缓存在页面请求期间持续存在,并在整个 Drupal 核心中使用。如果您在请求期间多次调用同一个函数但不希望输出发生变化,这将非常有用。一个典型的例子是加载需要在页面不同区域显示的节点时,如果节点出现在页面上和相关链接块中,这种情况并不少见。显然,一旦 Drupal 加载了一个节点,第二次加载它几乎没有什么好处,因此该drupal_static()函数用于将数据存储在静态缓存中,并在需要时再次检索它。这是一个非常简单的drupal_static()实际操作示例。

function my_module_load() {
  static $my_module_data;
  $my_module_data = &drupal_static(__FUNCTION__);
  if (empty($my_module_data)) {
    // 运行昂贵的操作并将数据添加到 $my_module_data 变量。
  }
  return $my_module_data;
}

上面代码中看到的__FUNCTION__是一个PHP魔术常量,代表函数名。该drupal_static()函数采用单个必需参数,即要存储的缓存项的名称。在上面的代码中,__FUNCTION__ 参数可以替换为 'my_module_load' 以保持相同的功能。如果您想更具体地使用此标签,您可以将其与参数连接起来,以便您仅在缓存中存储和检索特定项目。

function my_module_load($id) {
  static $my_module_data;
  $my_module_data = &drupal_static(__FUNCTION__ . $id);
  if (empty($my_module_data)) {
    // 运行昂贵的操作并将数据添加到 $my_module_data 变量。
  }
  return $my_module_data;
}

另一种方法是在函数顶部添加一个块以立即返回静态缓存(如果存在),而不是包装函数内容的 if 语句。

function my_module_load($id) {
  static $my_module_data;
  $my_module_data = &drupal_static(__FUNCTION__ . $id);
  if (!empty($my_module_data)) {
    return $my_module_data;
  }
 
  // 运行昂贵的操作并将数据添加到 $my_module_data 变量。
  return $my_module_data;
}

请记住,您可以在页面请求期间多次调用此函数,但您将始终收到相同的返回值。只有在后续页面请求中,如果您的数据不同,该值才会更改。

如果您需要清除静态缓存,您可以运行该drupal_static_reset()函数,将要清除的静态缓存的名称传递给它。如果您使用了 __FUNCTION__ 魔术常量,您可以将其替换为您定义缓存的函数的名称。

drupal_static_reset('my_module_load');

更永久的缓存

静态缓存很好,但是当您允许将缓存内容保存到磁盘(或持久缓存)并在后续页面视图中读取时,可以看到速度的真正提高。这是cache_set()和cache_get()发挥作用。

该cache_set()函数是这两个函数中最复杂的,用于(顾名思义)将任何数据写入缓存。这个函数需要一些参数:

  • $cid:(必需)cache_set()函数中的第一个参数是缓存的名称,用于再次检索缓存。

  • $data:(必需)第二个参数是要缓存的数据。需要注意的是,复杂的数据类型(如数组和对象)将在存储之前自动序列化。从缓存返回时,任何序列化数据都将被反序列化,但您需要确保包含的任何对象__sleep()以及__wake()将对象恢复到原始状态的方法。

  • $bin:Drupal 中有许多可用的缓存,称为“bins”。此函数的默认行为是将缓存写入主缓存 bin,默认情况下是 Drupal 数据库中名为“cache”的表。这可以更改为许多不同的 bin,但您可能会使用默认的 'cache' 或 'cache_block' 进行块缓存。

  • $expire:这告诉 Drupal 将缓存数据保留多长时间。

    • CACHE_PERMANENT:表示除非明确告知要cache_clear_all()与缓存 ID一起使用,否则不应删除该项目。

    • CACHE_TEMPORARY:指示应在下一次常规缓存擦除时删除该项目。

    • Unix 时间戳:表示该项目应至少保留到给定时间,之后它的行为类似于 CACHE_TEMPORARY。

您通常会cache_set()像这样使用该函数。

cache_set('my_module_cache', $data, 'cache', CACHE_TEMPORARY);

该cache_get()函数返回缓存的内容并接受两个参数:

  • $cid:(必需)这是缓存的名称,它应该与您在使用时使用的值相同cache_set()。

  • $bin:这是一个可选参数,允许您选择不同的缓存箱来获取缓存。同样,这应该与您用来将缓存存储在cache_set().

该cache_get()函数不仅包含您存储在缓存中的数据,它还是一个包含您存储的数据和有关该数据的其他一些信息的对象。这是缓存对象的典型示例:

stdClass Object
(
    [cid] => my_module_cache
    [data] => text
    [created] => 1427730490
    [expire] => -1
    [serialized] => 0
)

这里的重要部分是“数据”参数,您应该访问它以获取存储的缓存值。如果缓存当前已过期 (expire) 以及原始值是否已序列化 (serialized),则其他参数会告诉您创建(创建)缓存时的缓存 ID (cid)。

下面是一个使用该cache_get()函数的简单示例。

$cache = cache_get($cache_id, 'cache');
$data = $cache->data;

当然,最好将这两个功能一起使用。下面是一个简单方法的示例,其中这些函数可用于为复杂输出创建缓存资源。我在这里创建了一些简单的标记来展示它的实际效果。

function my_module_create_markup() {
 
  // 从缓存后端检索。
  if ($cache = cache_get('my_module_cache', 'cache')) {
    $markup = $cache->data;
  }
 
  // 如果我们还没有缓存版本,则构建标记。
  if (empty($markup)) {
    $markup = '';
 
    // 做一些事情并将其添加到标记变量中。
    $markup .= date('c');
 
    for ($i = 0; $i < 10; ++$i) {
      $markup .= pow($i, 10) . ' ';
    }
  }
 
  // 使用 cache_id 缓存输出(CACHE_TEMPORARY 表示可以在缓存清除时将其删除)。
  cache_set('my_module_cache', $markup, 'cache', CACHE_TEMPORARY);
 
  // 返回标记,无论它是谁生成的。
  return $markup;
}

将所有这些功能放在一起时,我通常会包含一个参数,我可以使用该参数通过单个配置设置打开或关闭缓存功能。这使开发变得更加容易,并允许您在模块处于活动状态时自动开始使用实时系统上的缓存。将缓存 ID 分离到一个变量中也是一种很好的做法,这允许创建基于上下文的缓存的可能性。这意味着如果您想使用用户输入来创建缓存,那么您不必对代码进行太多修改。drupal_static()除了缓存函数之外,我还倾向于使用该函数,因为这为对同一函数的多次调用提供了进一步的弹性。

这是一个功能齐全的缓存功能的示例。

function my_module_create_markup() {
  // 初始化静态
  static $markup;
 
  // 检查是否应该使用缓存。
  $use_cache = variable_get('my_module_use_cache', TRUE);
 
  // 设置缓存 ID 以存储和检索缓存。
  $cache_id = 'my_module_cache';
 
  if ($use_cache) {
 
    // 尝试从静态缓存中检索。
    $markup = &drupal_static($cache_id);
 
    if (empty($markup)) {
      // 从缓存后端检索。
      if ($cache = cache_get($cache_id, 'cache')) {
        $markup = $cache->data;
      }
    }
  }
 
  // 如果我们还没有缓存版本,则构建标记。
  if (empty($markup)) {
 
    // 初始化标记变量。
    $markup = '';
 
    // 做一些事情并将其添加到标记变量中。
    $markup .= date('c');
    for ($i = 0; $i < 10; ++$i) {
      $markup .= pow($i, 10) . ' ';
    }
  }
 
  // 缓存这个标记?
  if ($use_cache) {
    // 使用 cache_id 缓存输出(CACHE_TEMPORARY 表示可以在缓存清除时将其删除)。
    cache_set($cache_id, $markup, 'cache', CACHE_TEMPORARY);
  }
 
  // 返回标记,无论它是谁生成的。
  return $markup;
}

Drupal 会在缓存数据过期时自动清除缓存数据,但您可以使用该cache_clear_all()功能强制清除缓存。这个函数需要三个可选参数,因为可以不带参数调用它来清除 Drupal 中的所有缓存。

  • $cid:这是您要清除的缓存的缓存 ID。本质上,cache_set()调用中的第一个参数。您还可以传递一组缓存 ID 以清除多个缓存。如果设置为 NULL,$wildcard 参数将被忽略。

  • $bin:这是您用来存储缓存的缓存箱。如果您提供了 $cid 参数,则这是必需的。

  • $wildcard:如果为 TRUE,则 $cid 参数必须包含字符串值,并且除了 $cid 指定的确切缓存 ID 之外,还删除以 $cid 开头的缓存 ID。如果 $wildcard 为 TRUE 且 $cid 为 '*',则整个缓存被清空。

当您知道某个更改会影响缓存的输出时,您会在代码的其他地方使用此函数。例如,您可能有一个部分显示某些节点,在这种情况下,您需要添加一个hook_node_presave()钩子,以便在用户保存节点时清除该部分的缓存。要清除在上述代码中创建的缓存,您将使用以下内容。

cache_clear_all('my_module_cache', 'cache');

显然,在 Drupal 中缓存可能会有点复杂,但是如果您围绕cache_set()和cache_get()函数设计代码,那么您可以创建响应式站点来存储重复数据,而无需重新生成所有内容。可以在 Drupal 中扩展可用的缓存箱,但强烈建议您使用cache_set()和cache_get()函数与所有缓存进行交互。即使是视图缓存(在 Drupal 中创建 cache_views 和 cache_views_data 作为缓存箱)仍然使用cache_get()从缓存中获取信息。

也可以改变 Drupal 中的缓存机制,将缓存写入不同的存储机制。这是通过使用 APC、Redis 和 Memcache 等模块来实现的,但也要求这些系统事先存在。