理解 WordPress rewrite_rules 在数据库中的缓存策略

WordPress Rewrite Rules 缓存策略深度解析

大家好,今天我们来深入探讨 WordPress 中 Rewrite Rules 的缓存策略。Rewrite Rules 是 WordPress 实现漂亮 URL(Permalinks)的核心机制。理解其缓存方式对于优化网站性能至关重要。我们会从 Rewrite Rules 的生成、存储、更新以及 WordPress 如何利用缓存来提升效率等方面进行详细讲解,并配合代码示例,帮助大家彻底理解这一机制。

1. Rewrite Rules 的生成与结构

Rewrite Rules 本质上是一组正则表达式和对应的查询字符串的映射关系,用于将用户友好的 URL 转换为 WordPress 可以理解的内部请求。

1.1 Rewrite Rules 的来源

Rewrite Rules 主要来源于以下几个方面:

  • WordPress 核心: 核心模块定义了基础的 Rewrite Rules,例如文章、页面、分类、标签等。
  • 主题: 主题可以添加自定义的 Rewrite Rules,用于处理主题特定的页面或功能。
  • 插件: 插件是最常见的 Rewrite Rules 添加者,它们可以为自定义文章类型、自定义分类法、以及其他各种功能添加 Rewrite Rules。

1.2 Rewrite Rules 的数据结构

Rewrite Rules 在 PHP 中以关联数组的形式存在,key 是正则表达式,value 是对应的查询字符串。例如:

Array
(
    'post/([0-9]+)/?$' => 'index.php?p=$matches[1]',
    'category/([^/]+)/?$' => 'index.php?category_name=$matches[1]'
)

这个例子中:

  • 'post/([0-9]+)/?$' 是一个正则表达式,匹配类似于 post/123/ 的 URL。
  • 'index.php?p=$matches[1]' 是对应的查询字符串,将匹配到的数字作为文章 ID (p) 传递给 index.php
  • 'category/([^/]+)/?$' 是另一个正则表达式,匹配类似于 category/news/ 的 URL。
  • 'index.php?category_name=$matches[1]' 是对应的查询字符串,将匹配到的分类名称作为 category_name 传递给 index.php

1.3 add_rewrite_rule() 函数

插件和主题通常使用 add_rewrite_rule() 函数来添加自定义的 Rewrite Rules。该函数的原型如下:

add_rewrite_rule( string $regex, string $redirect, string $priority = 'bottom' )
  • $regex: 正则表达式。
  • $redirect: 查询字符串。
  • $priority: 优先级,可以是 topbottomtop 表示添加到规则列表的顶部,bottom 表示添加到规则列表的底部。 优先级越高,规则匹配的顺序越早。

示例:

function my_plugin_add_rewrite_rule() {
    add_rewrite_rule(
        '^my-custom-page/?$',
        'index.php?my_custom_page=1',
        'top'
    );
}
add_action( 'init', 'my_plugin_add_rewrite_rule' );

function my_plugin_query_vars( $query_vars ) {
    $query_vars[] = 'my_custom_page';
    return $query_vars;
}
add_filter( 'query_vars', 'my_plugin_query_vars' );

这段代码添加了一个 Rewrite Rule,将 my-custom-page/ 映射到 index.php?my_custom_page=1,并且将 my_custom_page 添加到允许的查询变量列表中。

2. Rewrite Rules 的存储:wp_options

Rewrite Rules 最终存储在 WordPress 数据库的 wp_options 表中。具体来说,它们存储在以下两个 option 中:

  • rewrite_rules: 存储主要的 Rewrite Rules。
  • category_base, tag_base, author_base: 存储分类、标签和作者的 URL 结构。

2.1 rewrite_rules option

rewrite_rules option 存储的是一个序列化的 PHP 数组,包含了所有的 Rewrite Rules。 当 WordPress 需要使用 Rewrite Rules 时,它会从数据库中读取这个 option,反序列化成 PHP 数组,然后进行匹配。

2.2 其他 base options

category_base, tag_base, author_base 等 option 用于存储分类、标签和作者的 URL 前缀。这些前缀会影响到相应的 Rewrite Rules 的生成。例如,如果 category_base 设置为 topics,那么分类的 URL 将会是 topics/分类名称/,而不是默认的 category/分类名称/

2.3 数据库查看

可以使用 SQL 查询语句来查看 rewrite_rules option 的内容:

SELECT option_value FROM wp_options WHERE option_name = 'rewrite_rules';

查询结果会是一个很长的字符串,是序列化的 PHP 数组。可以使用 PHP 的 unserialize() 函数将其反序列化,以便查看其结构。

3. Rewrite Rules 的更新与刷新

Rewrite Rules 不是静态不变的。当以下情况发生时,Rewrite Rules 需要被更新:

  • 添加、删除或修改文章分类、标签或页面。
  • 更改 Permalink 设置。
  • 激活、停用或更新主题或插件,这些主题或插件可能会添加或删除 Rewrite Rules。
  • 手动调用 flush_rewrite_rules() 函数。

3.1 flush_rewrite_rules() 函数

flush_rewrite_rules() 函数是用于刷新 Rewrite Rules 的核心函数。它的作用是:

  1. 删除 rewrite_rules option。
  2. 重新生成所有的 Rewrite Rules。
  3. 将新的 Rewrite Rules 存储到 rewrite_rules option 中。

flush_rewrite_rules() 函数是一个耗时的操作,因为它需要遍历所有的文章、分类、标签等,并根据当前的 Permalink 设置生成 Rewrite Rules。 因此,应该避免频繁调用该函数。

3.2 插件激活和停用时的刷新

插件在激活和停用时,通常会调用 flush_rewrite_rules() 函数,以确保 Rewrite Rules 的正确性。 理想情况下,插件应该在激活时添加 Rewrite Rules,在停用时删除 Rewrite Rules。但并非所有插件都遵循最佳实践,有些插件可能会在每次页面加载时都尝试添加 Rewrite Rules,这会导致性能问题。

3.3 Permalink 设置更改时的刷新

当在 WordPress 后台更改 Permalink 设置时,WordPress 会自动调用 flush_rewrite_rules() 函数,以更新 Rewrite Rules。

3.4 手动刷新

在某些情况下,可能需要手动调用 flush_rewrite_rules() 函数。例如,在开发自定义主题或插件时,如果添加或修改了 Rewrite Rules,可以手动调用该函数来更新 Rewrite Rules。

示例:

function my_plugin_activate() {
    my_plugin_add_rewrite_rule(); // 添加 Rewrite Rule
    flush_rewrite_rules(); // 刷新 Rewrite Rules
}
register_activation_hook( __FILE__, 'my_plugin_activate' );

function my_plugin_deactivate() {
    // 移除 Rewrite Rule 的代码 (可选)
    flush_rewrite_rules(); // 刷新 Rewrite Rules
}
register_deactivation_hook( __FILE__, 'my_plugin_deactivate' );

这段代码在插件激活时添加 Rewrite Rule 并刷新,在插件停用时刷新。 建议在停用时移除对应的 Rewrite Rules,以避免产生不必要的规则。

3.5 WP_Rewrite

WordPress 使用 WP_Rewrite 类来管理 Rewrite Rules。可以通过全局变量 $wp_rewrite 来访问该类的实例。

WP_Rewrite 类提供了以下方法:

  • wp_rewrite->rules: 存储 Rewrite Rules 的数组。
  • wp_rewrite->flush_rules(): 刷新 Rewrite Rules (等同于 flush_rewrite_rules() 函数)。
  • wp_rewrite->wp_rewrite_rules(): 生成核心的 Rewrite Rules。
  • wp_rewrite->mod_rewrite_rules(): 生成 .htaccess 文件中的 Rewrite Rules 代码 (如果启用了 Apache 的 mod_rewrite 模块)。

4. Rewrite Rules 的匹配过程

当用户访问一个 URL 时,WordPress 会按照以下步骤进行 Rewrite Rules 的匹配:

  1. 获取请求的 URI。 例如,如果用户访问 http://example.com/post/123/,那么请求的 URI 就是 /post/123/
  2. 从数据库中读取 rewrite_rules option,并反序列化成 PHP 数组。
  3. 遍历 Rewrite Rules 数组,依次使用正则表达式匹配请求的 URI。
  4. 如果匹配成功,则使用对应的查询字符串来构建 WordPress 的内部请求。
  5. WordPress 根据内部请求来处理页面,并生成响应。

4.1 .htaccess 文件

如果启用了 Apache 的 mod_rewrite 模块,WordPress 还会将 Rewrite Rules 写入到 .htaccess 文件中。 这样做的好处是,Apache 可以直接处理 Rewrite Rules,而无需每次都将请求传递给 WordPress。 这可以提高网站的性能。

.htaccess 文件位于 WordPress 网站的根目录下。 它的内容类似于:

<IfModule mod_rewrite.c>
RewriteEngine On
RewriteBase /
RewriteRule ^index.php$ - [L]
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule . /index.php [L]
</IfModule>

这些规则告诉 Apache,如果请求的文件或目录不存在,则将请求传递给 index.php

4.2 Nginx 配置

如果使用 Nginx 作为 Web 服务器,则需要手动配置 Rewrite Rules。 Nginx 的 Rewrite Rules 语法与 Apache 的不同。 可以参考 WordPress 官方文档或其他资源来配置 Nginx 的 Rewrite Rules。

5. Rewrite Rules 缓存策略

WordPress 的 Rewrite Rules 缓存策略比较简单:

  1. 数据库缓存: Rewrite Rules 存储在 wp_options 表中,可以利用 WordPress 对象缓存(Object Cache)或数据库查询缓存来提高读取速度。
  2. 文件缓存 (.htaccess): 如果使用 Apache 并且启用了 mod_rewrite,Rewrite Rules 会写入到 .htaccess 文件中,Apache 会缓存这些规则,从而避免每次都从数据库中读取。
  3. Opcode 缓存: PHP 的 Opcode 缓存(例如 OPcache)可以缓存编译后的 PHP 代码,包括用于处理 Rewrite Rules 的代码,从而提高性能。
  4. 对象缓存 (Object Cache): WordPress 对象缓存可以缓存从数据库中读取的数据,包括 rewrite_rules option 的值,从而减少数据库查询次数。

5.1 利用对象缓存

可以使用 WordPress 对象缓存来缓存 rewrite_rules option 的值。 常用的对象缓存插件包括:

  • Memcached
  • Redis

这些插件可以将 rewrite_rules option 的值存储在内存中,从而提高读取速度。

5.2 .htaccess 缓存

Apache 会缓存 .htaccess 文件,因此 Rewrite Rules 的修改不会立即生效。 可以通过重启 Apache 或清除 Apache 的缓存来使修改生效。

5.3 避免频繁刷新

如前所述,flush_rewrite_rules() 函数是一个耗时的操作,应该避免频繁调用。 应该只在必要时才调用该函数,例如在插件激活和停用时,或者在更改 Permalink 设置时。

6. 性能优化建议

  • 使用对象缓存: 使用 Memcached 或 Redis 等对象缓存插件来缓存 rewrite_rules option 的值。
  • 避免频繁刷新: 避免频繁调用 flush_rewrite_rules() 函数。
  • 优化 Rewrite Rules: 编写高效的正则表达式,避免使用过于复杂的规则。
  • 使用 CDN: 使用 CDN 来缓存静态资源,例如 CSS、JavaScript 和图片。
  • 选择合适的 Web 服务器: Nginx 通常比 Apache 更快,尤其是在处理静态资源方面。
  • 启用 Gzip 压缩: 启用 Gzip 压缩可以减小 HTTP 响应的大小,从而提高网站的加载速度。
  • 使用 PHP 7 或更高版本: PHP 7 比 PHP 5 快得多,可以显著提高网站的性能。

7. 调试 Rewrite Rules

调试 Rewrite Rules 可能会比较困难,因为 Rewrite Rules 的匹配过程是隐藏的。 以下是一些调试 Rewrite Rules 的技巧:

  • 使用 var_dump()print_r() 函数来查看 rewrite_rules option 的内容。
  • 使用 WP_DEBUG 常量来启用 WordPress 的调试模式。
  • 使用插件来查看 Rewrite Rules 的匹配过程。 例如,Query Monitor 插件可以显示当前请求匹配到的 Rewrite Rule。
  • 查看 Apache 或 Nginx 的日志文件,以了解 Rewrite Rules 的匹配情况。

8. 代码示例:自定义 Rewrite Rules 和对象缓存

以下是一个代码示例,演示了如何添加自定义的 Rewrite Rules,并使用对象缓存来提高性能。

<?php
/**
 * Plugin Name: My Custom Rewrite Rules
 * Description: Example of custom rewrite rules and object caching.
 */

// 添加 Rewrite Rule
function my_plugin_add_rewrite_rule() {
    add_rewrite_rule(
        '^my-custom-page/([0-9]+)/?$',
        'index.php?my_custom_page_id=$matches[1]',
        'top'
    );
}
add_action( 'init', 'my_plugin_add_rewrite_rule' );

// 添加查询变量
function my_plugin_query_vars( $query_vars ) {
    $query_vars[] = 'my_custom_page_id';
    return $query_vars;
}
add_filter( 'query_vars', 'my_plugin_query_vars' );

// 插件激活时刷新 Rewrite Rules
function my_plugin_activate() {
    my_plugin_add_rewrite_rule();
    flush_rewrite_rules();
}
register_activation_hook( __FILE__, 'my_plugin_activate' );

// 插件停用时刷新 Rewrite Rules
function my_plugin_deactivate() {
    flush_rewrite_rules();
}
register_deactivation_hook( __FILE__, 'my_plugin_deactivate' );

// 处理自定义页面
add_action( 'template_redirect', 'my_plugin_template_redirect' );
function my_plugin_template_redirect() {
    $my_custom_page_id = get_query_var( 'my_custom_page_id' );

    if ( $my_custom_page_id ) {
        // 尝试从对象缓存中获取数据
        $cached_data = wp_cache_get( 'my_custom_page_data_' . $my_custom_page_id, 'my_plugin' );

        if ( false === $cached_data ) {
            // 如果缓存中没有数据,则从数据库中获取
            $cached_data = my_plugin_get_data_from_database( $my_custom_page_id );

            // 将数据存储到对象缓存中,有效期为 1 小时
            wp_cache_set( 'my_custom_page_data_' . $my_custom_page_id, $cached_data, 'my_plugin', 3600 );
        }

        // 显示数据
        echo '<h1>My Custom Page</h1>';
        echo '<p>ID: ' . esc_html( $my_custom_page_id ) . '</p>';
        echo '<p>Data: ' . esc_html( $cached_data ) . '</p>';
        exit;
    }
}

// 从数据库中获取数据 (示例)
function my_plugin_get_data_from_database( $id ) {
    // 这里应该从数据库中获取数据,例如从自定义表中获取
    // 为了演示,这里直接返回一个字符串
    return 'Data for ID: ' . $id;
}

代码解释:

  1. 添加 Rewrite Rule: my_plugin_add_rewrite_rule() 函数添加了一个 Rewrite Rule,将 my-custom-page/([0-9]+)/?$ 映射到 index.php?my_custom_page_id=$matches[1]
  2. 添加查询变量: my_plugin_query_vars() 函数将 my_custom_page_id 添加到允许的查询变量列表中。
  3. 刷新 Rewrite Rules: my_plugin_activate()my_plugin_deactivate() 函数分别在插件激活和停用时刷新 Rewrite Rules。
  4. 处理自定义页面: my_plugin_template_redirect() 函数根据 my_custom_page_id 查询变量来处理自定义页面。
  5. 对象缓存: my_plugin_template_redirect() 函数首先尝试从对象缓存中获取数据。如果缓存中没有数据,则从数据库中获取数据,并将数据存储到对象缓存中,有效期为 1 小时。
  6. 获取数据: my_plugin_get_data_from_database() 函数从数据库中获取数据(这里只是一个示例,实际应该根据具体情况从数据库中获取数据)。

9. 总结要点

Rewrite Rules 是 WordPress 实现漂亮 URL 的重要机制,存储在 wp_options 表中,可以通过对象缓存和 .htaccess 文件进行缓存。了解 Rewrite Rules 的生成、存储、更新和匹配过程,可以帮助我们更好地优化 WordPress 网站的性能。

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注