Partial Reindex Strategies in Magento 2: Mview, Changelogs, Targeted Reindexing
AI generated
M2
di.xml
Magento 2 · Indexer · Performance · Mview
Partial Reindex Strategies in Magento 2
Mview, changelogs and targeted reindexing instead of full reindex

With a catalog of hundreds of thousands of products, a full reindex costs time many shops simply cannot afford. Anyone who consistently applies partial reindex strategies processes only the records that actually changed and keeps indexers within a manageable time frame even as the catalog grows large.

18 min read Mview · changelog · executeList · CLI Magento 2.4.x

1. Why full reindex hits limits with large catalogs

A full reindex processes every single record without exception, regardless of whether it changed since the last run or not. With a catalog of a few hundred products this barely registers, but with several hundred thousand products a single full reindex run can take hours. This is exactly where partial reindex strategies come in: instead of recomputing everything, only the subset that actually changed gets processed.

The effect of partial reindex strategies is especially clear in catalogs where only a small percentage of products are updated daily, for example through price changes from individual suppliers or isolated stock corrections. A full reindex in this scenario would needlessly recompute the remaining 95 percent of unchanged products, wasting server resources that are urgently needed elsewhere.

Magento did not bolt partial reindex strategies on as an afterthought performance hack, but built them in as an integral part of the indexer architecture through the Mview mechanism. Anyone who understands and correctly configures this mechanism gets partial reindex essentially for free, without building additional custom infrastructure.

2. Mview as the technical foundation for partial reindex

Mview, short for materialized view, is the core mechanism behind every partial reindex strategy in Magento. For every indexer registered in mview.xml, Magento creates a changelog table and attaches database triggers to the source tables declared there. Every relevant change, whether insert, update or delete, writes the affected entity ID into this changelog table, tagged with a sequential version number.

The reindex process reads this changelog table starting from the last processed version number, collects all IDs added since then, deduplicates them, and calls executeList with this compact list. For solid partial reindex strategies, it is crucial that truly all relevant source tables are covered in mview.xml, since every missing table means blind spots where changes go unnoticed.

3. Understanding changelog tables in detail

The changelog table of an indexer follows the naming convention {view_id}_cl and essentially contains two columns: an auto incrementing version_id and the affected entity ID. A new entry is created for every change to a watched source table, even if the same entity ID is changed several times within a short period. The processing step deduplicates these IDs before calling executeList, so a product saved three times in a row still only gets recomputed once.


-- Inspect the changelog table for a custom indexer directly
SELECT version_id, entity_id
FROM vendor_pricematrix_cl
ORDER BY version_id DESC
LIMIT 50;

-- Count pending changes since a given version marker
SELECT COUNT(DISTINCT entity_id) AS pending_ids
FROM vendor_pricematrix_cl
WHERE version_id > 128340;

-- Check the current version marker Magento has already processed
SELECT * FROM mview_state WHERE view_id = 'vendor_pricematrix';

After successful processing, changelog entries are not deleted immediately, but follow their own cleanup routine that periodically removes old entries. For partial reindex strategies, it is important to know that the mview_state table stores the last processed version per view; this is the central place to check whether an indexer is actually up to date.

4. Implementing partial reindex in your own indexer

For custom indexers, a working partial reindex strategy means executeList actually efficiently processes only the given IDs, instead of internally still iterating over every record. A common implementation mistake is declaring executeList correctly but running an internal query without a WHERE entity_id IN (...) filter, which turns the supposed partial reindex into a full reindex under a false name.


<?php
declare(strict_types=1);

namespace Vendor\PriceMatrix\Model\ResourceModel\PriceMatrix;

use Magento\Framework\App\ResourceConnection;

/**
 * Performs the actual price calculation for a bounded set of product IDs,
 * always filtering by entity_id to keep partial reindex genuinely partial.
 */
class PriceCalculator
{
    private const TARGET_TABLE = 'vendor_pricematrix';

    /**
     * @param ResourceConnection $resourceConnection Provides the write connection.
     */
    public function __construct(
        private readonly ResourceConnection $resourceConnection
    ) {
    }

    /**
     * Recalculates prices only for the given product IDs, never for the full catalog.
     *
     * @param int[] $ids Product IDs affected by the change.
     * @return void
     */
    public function rebuildForIds(array $ids): void
    {
        if ($ids === []) {
            return;
        }

        $connection = $this->resourceConnection->getConnection();
        $table = $this->resourceConnection->getTableName(self::TARGET_TABLE);

        // Delete only the affected rows, then recompute exactly those
        $connection->delete($table, ['product_id IN (?)' => $ids]);

        $select = $connection->select()
            ->from(['p' => $this->resourceConnection->getTableName('catalog_product_entity')])
            ->where('p.entity_id IN (?)', $ids);

        // ... aggregate and insert freshly computed rows for exactly these IDs
    }
}

This partial reindex strategy only works if every layer of processing, from the action class down to the database query, consistently works with the given ID list and never accidentally falls back to the entire dataset anywhere.

5. Batching: splitting large ID lists sensibly

During a bulk import or a large price update, tens of thousands of IDs can end up in the changelog table at once. A partial reindex strategy that tries to process all these IDs in a single database query with a huge IN (...) clause risks timeouts or excessive memory consumption. The established approach is batching: the ID list is split into manageable chunks of a few hundred to a few thousand IDs, processed one after another.

Batching within partial reindex strategies not only reduces the risk of timeouts but also allows caching progress along the way. If processing aborts halfway through the batches, for example due to an error or a server restart, the next run can resume from the last successfully processed batch instead of starting over, provided progress is logged accordingly.

6. Targeted reindex via CLI parameters

Besides automatic Mview based partial reindex, the Magento CLI also offers the ability to manually reindex individual records on purpose, for example after a data import without trigger involvement, or for debugging purposes. This manual variant is also part of pragmatic partial reindex strategies, especially in migration projects where data is imported directly via SQL and triggers are therefore never fired.


# Trigger partial reindex for specific product IDs manually
bin/magento indexer:reindex catalog_product_price --id 101,102,103

# Reindex only rows changed since a specific point via direct row processing
bin/magento indexer:reindex catalogsearch_fulltext --id 555

# Check current mview state for all registered views
bin/mysql -e "SELECT view_id, version_id, mode FROM mview_state;"

# Force Magento to re-detect changelog entries after a manual bulk import
bin/magento indexer:reset vendor_pricematrix
bin/magento indexer:reindex vendor_pricematrix

7. Common pitfalls when partial reindex fails silently

The most common mistake that silently undermines a partial reindex strategy is a bulk import via direct SQL insert or update that bypasses Mview's database triggers. Since triggers react to concrete SQL operations, not to the business event of data having changed, the changelog table stays empty in this case even though the data has very much changed. The indexer keeps reporting a valid status, even though it is factually stale.

A second common pitfall is an incomplete subscriptions declaration in mview.xml, where a relevant source table was simply forgotten. Changes to that table then remain completely invisible to the partial reindex strategy. Regular spot checks, comparing a manual full reindex against the current incremental state, reliably surface such gaps before they turn into serious data problems.

8. Monitoring changelog size and processing state

For sustainable partial reindex strategies, monitoring that tracks changelog table size over time is worthwhile. If a changelog table keeps growing without the entry count shrinking through regular processing, that points to a hung or disabled cron job that no longer processes the associated Mview view.

A simple threshold alert triggering when a changelog table exceeds a few tens of thousands of unprocessed entries catches most practical problems early, before the next full reindex takes surprisingly long or frontend data visibly goes stale.

9. Partial reindex compared to full reindex

Both approaches have their place, depending on the situation and data volume.

Criterion Partial reindex Full reindex
Runtime with large catalog Short, proportional to changes Long, proportional to total size
Fit after bulk import without triggers Unsuitable, changelog stays empty Reliable, processes everything
Resource usage Low with few changes High, regardless of change volume
Repairing data inconsistency Does not surface undetected gaps Guarantees a consistent state

The most pragmatic combination is using partial reindex strategies for daily operation and scheduling a periodic, for example weekly, full reindex as a safety net. This full reindex catches exactly the cases where triggers were bypassed or an Mview subscription was incomplete, ensuring undetected gaps do not accumulate over weeks.

Mironsoft

Magento 2 indexer performance and scaling

Does your reindex take longer than your catalog should grow?

We analyze existing indexers, identify hidden full reindex behavior disguised as partial reindex, and set up monitoring for changelog tables so your shop stays performant even through strong catalog growth.

Performance audit

Measuring reindex runtimes and identifying bottlenecks

Mview fixes

Fixing missing subscriptions and inefficient executeList implementations

Monitoring setup

Keeping changelog size and processing state under permanent watch

10. Summary

Effective partial reindex strategies in Magento 2 rest on a correctly configured Mview subscription, an action class where executeList actually efficiently processes only the given IDs, and a batching mechanism for large ID volumes. Where triggers do not apply, for example with direct SQL bulk imports, a periodic full reindex remains an indispensable safety net.

The biggest lever is consistently restricting every layer of the implementation to the given ID list, instead of internally iterating over the entire dataset after all. Anyone who consistently implements partial reindex strategies and regularly checks for undetected gaps keeps reindex runtimes within a plannable range even as the catalog grows substantially.

Partial reindex strategies in Magento 2, the essentials at a glance

Mview foundation

Changelog tables and triggers capture changes event based, all relevant tables must be covered.

Efficient implementation

executeList must consistently filter by ID, otherwise partial reindex is full reindex under a false name.

Batching

Split large ID lists into manageable chunks to avoid timeouts and memory problems.

Safety net

Periodic full reindex catches gaps from bypassed triggers or incomplete subscriptions.

11. FAQ: Partial Reindex Strategies in Magento 2

1Partial vs. full reindex?
Partial processes only changed records, full processes everything without exception.
2Changelog empty after bulk import?
Direct SQL imports bypass triggers, manual full reindex or --id needed.
3Verify real partial reindex?
Runtime should scale with ID count, check SQL queries for entity_id filtering.
4Optimal batch size?
500 to 2000 IDs per batch as a good starting point, depending on complexity.
5Still need full reindex?
Yes, periodically as a safety net against bypassed triggers.
6Find version state?
Table mview_state, filtered by view_id.
7Reindex individual IDs manually?
bin/magento indexer:reindex indexer_code --id 101,102,103.
8All source tables covered?
Regular spot checks with a known change and verifying the changelog table.
9Large changelog slows shop down?
Not directly, but lengthens the next processing run.
10Permanently invalid after import?
indexer:reset followed by a full indexer:reindex restores a consistent baseline.