mcpField
M
magento-indexing-caching

magento-indexing-caching

github.com/finsilabs开发6075 次安装5 天前更新
代码仓库

关于 magento-indexing-caching

面向 Magento 开发者和运维人员的索引与缓存配置技能,涵盖索引器管理、Varnish 全页缓存、Redis 应用缓存及缓存失效排查。

主要功能

  1. 01查看和管理索引器状态与模式
  2. 02执行单个或批量重建索引
  3. 03配置 Varnish 全页缓存
  4. 04配置 Redis 缓存与会话存储
  5. 05使用缓存标签失效相关页面

适用场景

  1. 01排查商品价格错误或商品不可见
  2. 02为生产环境配置 Varnish 和 Redis
  3. 03管理大型商品目录的计划索引
  4. 04在目录或 CMS 更新后清理相关缓存

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/finsilabs/awesome-ecommerce-skills --skill magento-indexing-caching

安装后在 Claude Code 中输入/magento-indexing-caching确认已加载。

包含的文件

除 SKILL.md 外,安装时一起下载的参考资料和脚本。

tile.json
evals/custom-module-cache-tag-invalidation/
criteria.json
task.md
evals/indexer-production-configuration/
criteria.json
task.md
evals/redis-cache-and-session-configuration/
criteria.json
task.md

详细介绍

Magento 索引与缓存

概述

Magento 的性能架构依赖两个不同的层:索引器会将反规范化数据(产品价格、搜索索引、类别路径)预先计算并存入扁平表;整页缓存(FPC)则存储完整渲染后的 HTML 页面。生产环境推荐使用 Varnish 作为 FPC 后端,取代 Magento 内置的基于文件的缓存。推荐使用 Redis 作为应用缓存、会话存储和默认缓存(区块 HTML、配置、布局)的后端。索引配置错误或数据过期,是导致价格错误和产品不可见问题的最常见原因之一。

适用场景

  • 商品目录更新后,产品显示的价格错误或不可见时
  • 首次在 Magento 生产服务上部署 Varnish 时
  • 排查因实时计算价格导致的类别页面加载缓慢问题时
  • 为 Magento 配置 Redis,用于会话和区块缓存存储时
  • 通过 Magento 的缓存标签,在商品目录或 CMS 更新后设置缓存失效时
  • 管理大型商品目录(> 50,000 个产品)的索引器计划,避免全量重新索引造成锁定时

核心说明

  1. 了解 Magento 的索引器及其模式

    # List all indexers and their status
    bin/magento indexer:info
    bin/magento indexer:status
    
    # Example output:
    # catalog_category_product    Category Products    valid    Update on Save
    # catalog_product_category    Product Categories   valid    Update on Save
    # catalog_product_price       Product Price        invalid  Update by Schedule
    # catalogsearch_fulltext      Catalog Search       valid    Update by Schedule
    # catalogrule_product         Catalog Rule Product invalid  Update on Save
    

    将所有生产环境索引器设为 Update by Schedule 模式,以避免阻塞结账流程:

    bin/magento indexer:set-mode schedule catalog_category_product
    bin/magento indexer:set-mode schedule catalog_product_price
    bin/magento indexer:set-mode schedule catalogsearch_fulltext
    bin/magento indexer:set-mode schedule catalog_product_flat
    bin/magento indexer:set-mode schedule catalog_category_flat
    
    # Verify modes
    bin/magento indexer:show-mode
    
  2. 管理索引器和部分重新索引

    # Reindex a single indexer (less disruptive than full reindex)
    bin/magento indexer:reindex catalog_product_price
    
    # Reindex all (avoid on production during business hours)
    bin/magento indexer:reindex
    
    # Reset indexer to "invalid" to force next scheduled run
    bin/magento indexer:reset catalog_product_price
    
    # Check mview (materialized view) changelog tables — lists pending rows to process
    bin/magento indexer:show-changelog
    

    对于超大型商品目录,请使用并行索引:

    # app/etc/env.php — enable parallel processing
    # In Admin → System → Index Management → Indexers → Configure
    # Or via config:
    bin/magento config:set dev/grid/async_indexing 1
    
    <?php
    // app/etc/env.php
    return [
        // ...
        'indexer' => [
            'batch_size' => [
                'catalog_product_price' => ['simple' => 200, 'configurable' => 50],
                'catalogsearch_fulltext' => ['simple' => 500],
            ],
        ],
    ];
    
  3. 将 Varnish 配置为整页缓存

    # In Admin → System → Configuration → Advanced → System → Full Page Cache
    # Set Caching Application: Varnish Cache
    # Export Varnish configuration:
    bin/magento varnish:vcl:generate --export-version=6 --output-file=/etc/varnish/magento.vcl
    

    生成的 VCL 中需要了解的关键部分:

    # /etc/varnish/magento.vcl (excerpts from generated config)
    
    sub vcl_recv {
        # Do not cache if private cookie is set (logged-in customer)
        if (req.http.cookie ~ "X-Magento-Vary=") {
            # Magento sets this cookie to vary cache by context (customer group, currency)
            # The cookie value changes only when the context changes — not per session
        }
    
        # Strip _ga, fbclid, utm_* params from cache key to prevent cache fragmentation
        set req.url = regsuball(req.url, "(\?|&)(utm_[^&]+|fbclid|gclid|mc_[^&]+)(&|$)", "\1");
    }
    
    sub vcl_hash {
        # Include Magento's context cookie in the cache hash
        if (req.http.cookie ~ "X-Magento-Vary=") {
            hash_data(regsub(req.http.cookie, ".*X-Magento-Vary=([^;]+).*", "\1"));
        }
    }
    
    sub vcl_backend_response {
        # Respect Magento's Cache-Control headers
        if (beresp.http.Cache-Control ~ "no-store") {
            set beresp.uncacheable = true;
            set beresp.ttl = 120s;
        }
    
        # Strip Set-Cookie on cacheable responses
        if (beresp.ttl > 0s) {
            unset beresp.http.set-cookie;
        }
    }
    
  4. 为应用缓存和会话配置 Redis

    <?php
    // app/etc/env.php — Redis cache and session configuration
    
    return [
        'cache' => [
            'frontend' => [
                'default' => [
                    'id_prefix' => 'b0c_',
                    'backend' => 'Magento\\Framework\\Cache\\Backend\\Redis',
                    'backend_options' => [
                        'server'            => '127.0.0.1',
                        'database'          => '0',
                        'port'              => '6379',
                        'password'          => '',
                        'compress_data'     => '1',
                        'compression_lib'   => 'gzip',
                        'persistent'        => 'mgto_cache',
                    ],
                ],
                'page_cache' => [
                    'id_prefix' => 'b0c_',
                    'backend' => 'Magento\\Framework\\Cache\\Backend\\Redis',
                    'backend_options' => [
                        'server'        => '127.0.0.1',
                        'database'      => '1', // Separate DB for FPC
                        'port'          => '6379',
                        'compress_data' => '0', // FPC data is already compressed HTML
                    ],
                ],
            ],
        ],
        'session' => [
            'save'                  => 'redis',
            'redis' => [
                'host'              => '127.0.0.1',
                'port'              => '6379',
                'password'          => '',
                'timeout'           => '2.5',
                'persistent_identifier' => 'mgto_sessions',
                'database'          => '2', // Separate DB for sessions
                'compression_threshold' => '2048',
                'compression_library'   => 'gzip',
                'log_level'             => '1',
                'max_concurrency'       => '6',
                'break_after_frontend'  => '5',
                'max_lifetime'          => '7200',
                'disable_locking'       => '1', // Improves performance but reduces session safety
            ],
        ],
    ];
    
  5. 为自定义模块实现缓存标签失效

    Magento 使用缓存标签,在数据变更时有选择地使缓存页面失效。自定义模块应正确添加标签并清理缓存:

    <?php
    // Declare cache tags in your block/model
    namespace MyVendor\CustomModule\Block;
    
    use Magento\Framework\View\Element\Template;
    use Magento\Framework\DataObject\IdentityInterface;
    
    class CustomProductWidget extends Template implements IdentityInterface
    {
        private array $productIds = [];
    
        public function getIdentities(): array
        {
            // Return cache tags so Magento invalidates this block when any of these products change
            $tags = [\Magento\Catalog\Model\Product::CACHE_TAG];
            foreach ($this->productIds as $id) {
                $tags[] = \Magento\Catalog\Model\Product::CACHE_TAG . '_' . $id;
            }
            return $tags;
        }
    }
    
    <?php
    // In your observer or command — flush only relevant pages after product update
    namespace MyVendor\CustomModule\Observer;
    
    use Magento\Framework\Event\ObserverInterface;
    use Magento\Framework\App\Cache\TypeListInterface;
    use Magento\PageCache\Model\Cache\Type as PageCacheType;
    
    class ProductSaveObserver implements ObserverInterface
    {
        public function __construct(
            private readonly TypeListInterface $cacheTypeList,
            private readonly \Magento\Framework\App\Cache\Tag\Resolver $tagResolver
        ) {}
    
        public function execute(\Magento\Framework\Event\Observer $observer): void
        {
            $product = $observer->getEvent()->getProduct();
    
            // Invalidate only pages tagged with this product's cache tag
            // Varnish will purge via PURGE request using X-Magento-Tags-Pattern header
            $this->cacheTypeList->invalidate(PageCacheType::TYPE_IDENTIFIER);
        }
    }
    

示例

诊断索引延迟导致价格错误的问题
# Check if mview changelog has pending rows
bin/magento indexer:show-changelog

# If catalog_product_price has > 0 pending rows, prices are stale
# Run the indexer to catch up
bin/magento indexer:reindex catalog_product_price

# Check cron_schedule table for missed cron jobs (should run indexers every minute)
bin/magento cron:run --group index &
# Or manually via:
mysql -u root -e "SELECT job_code, status, finished_at FROM magento.cron_schedule WHERE job_code LIKE '%index%' ORDER BY finished_at DESC LIMIT 20;"
部署后预热 FPC
#!/bin/bash
# scripts/warm-cache.sh — warm the FPC after deploy
set -e

MAGENTO_BASE_URL="https://www.mystore.com"
SITEMAP_URL="$MAGENTO_BASE_URL/sitemap.xml"

echo "Flushing Magento caches..."
bin/magento cache:flush

echo "Warming FPC via sitemap..."
# Download sitemap and curl each URL with a warm-cache header
curl -s "$SITEMAP_URL" | \
  grep -oP '<loc>\K[^<]+' | \
  head -500 | \
  xargs -P 10 -I {} curl -s -o /dev/null -w "%{http_code} {}\n" \
    -H "X-Warm-Cache: 1" {}

echo "Cache warming complete."

最佳实践

  • 在生产环境始终使用 Update by Schedule 模式 — Update on Save 会在管理员保存时同步重新索引,大型商品目录可能因此超时
  • 为缓存、FPC 和会话使用独立的 Redis 数据库 — 所有内容都使用数据库 0 会导致键冲突,也无法单独清理某一类缓存
  • 使用 IdentityInterface 为自定义区块添加标签 — 这样在数据变更时,Varnish 和 Magento 的 FPC 才能使正确的页面失效
  • 监控索引器变更日志表的大小 — catalog_product_price_cl 表中有数百万行,说明 cron 未运行;应在表过大导致重新索引超时前修复 cron
  • 使用 bin/magento cache:clean,而不是 cache:flush — clean 只会移除无效的缓存条目(安全);flush 会清空所有内容,包括与其共用 Redis 的其他框架缓存
  • 为每个环境设置不同的 id_prefix — 如果每个环境都使用唯一前缀,预发布环境和生产环境可以共用 Redis 服务,从而防止缓存污染
  • 使用 curl -I 和 X-Cache 标头测试 Varnish — 响应为 HIT 表示 Varnish 正在从缓存提供内容;MISS 表示每个请求都在访问 Magento

常见问题

问题解决方案
导入后商品不可见或价格错误批量导入后运行 bin/magento indexer:reindex——如果直接使用 ResourceModel\Product::save,以编程方式保存商品并不总会触发索引器
Varnish 向已登录的客户提供过时页面确保 Varnish VCL 的哈希中包含 X-Magento-Vary cookie;已登录的客户会获得不同的 vary 值,从而绕过共享缓存
Redis 内存不足对缓存数据库,在 redis.conf 中设置 maxmemory-policy allkeys-lru;对会话数据库,使用 noeviction 以防会话静默丢失
保存商品后 FPC 未失效检查 Varnish 的 PURGE ACL 是否允许来自 Magento 服务 IP 的请求;使用 curl -X PURGE http://varnish-ip/ 进行测试
全量重新索引耗时数小时并锁定表在 env.php 中启用索引器批次大小,并使用 --mode realtime 运行以进行部分重新索引;安排在非高峰时段执行
Cron 索引器未运行检查 var/log/magento.cron.log 中的错误;确认系统 crontab 条目会以 Web 服务器用户身份每分钟运行一次 bin/magento cron:run

相关技能

  • @magento-module-development
  • @magento-graphql
  • @magento-multi-store
  • @caching-strategies
  • @infrastructure-performance

评论

支持 Markdown