Magento 2 MSI en de inventory_stock_2-tabel – de stille killer van indexeerprestaties

Zeer trage indexering in Magento 2

2 minuten, 48 seconden

Magento 2 MSI en de inventory_stock_2-tabel – de stille killer van indexeerprestaties

Magento 2 MSI en de inventory_stock_2-tabel – de stille killer van indexeerprestaties

In een van onze recente Magento 2-implementaties (ongeveer 200.000 producten, Multi Source Inventory ingeschakeld) zagen we zeer lange indexeringstijden voor de modules:

  • Inventory
  • Product Price
  • Category Products

Alle klassieke indexen leken correct. MariaDB had 256 GB RAM, de buffer pool was ruim genoeg, de EAV-indexen waren aanwezig, en toch bleven zware queries terugkomen rond:

inventory_stock_2

Na een diepere analyse bleek dat het probleem niet de hoeveelheid data was, maar de manier waarop Magento MSI de tabel inventory_stock_* opbouwt.


Waar komt de inventory_stock_2-tabel vandaan?

De tabel inventory_stock_2 verschijnt in Magento zodra Multi Source Inventory (MSI) is ingeschakeld. Magento maakt voor elke stock een aparte tabel aan:

  • inventory_stock_1
  • inventory_stock_2
  • inventory_stock_3 ...

De tabel wordt dynamisch aangemaakt door de klasse:

Magento\InventoryIndexer\Indexer\IndexStructure

en ziet er ongeveer zo uit:

CREATE TABLE inventory_stock_2 (
  sku varchar(64) NOT NULL,
  quantity decimal(12,4) NOT NULL DEFAULT 0.0000,
  is_salable tinyint(1) NOT NULL,
  PRIMARY KEY (sku),
  KEY index_sku_qty (sku, quantity)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_general_ci;

Wat is precies het probleem?

Er zijn twee kernproblemen:

1. Geen utf8mb4

De kolom sku wordt vaak aangemaakt met utf8_general_ci, terwijl in catalog_product_entity meestal dit staat:

utf8mb4_general_ci

Dat veroorzaakt:

  • karakterconversie tijdens JOINs
  • gebruik van join buffer
  • slechtere queryplannen

2. Geen index die begint met is_salable

Magento voert heel vaak queries uit zoals:

WHERE stock_index.is_salable = 0
OR stock_index.is_salable IS NULL

En omdat de enige index

(sku, quantity)

is, kan MariaDB niet efficiënt filteren op is_salable.

Gevolg:

  • volledige table scans
  • lange DELETE-acties tijdens de price index
  • lock wait timeout 1205
  • inventory indexer blijft eindeloos op "processing" staan

Waarom werkt een handmatige ALTER TABLE niet?

Omdat Magento bij iedere indexering het volgende doet:

  • DROP TABLE inventory_stock_X
  • CREATE TABLE inventory_stock_X

Elke handmatige wijziging verdwijnt dus weer.

Daarom moet de oplossing zijn: de tabel automatisch aanpassen direct na het aanmaken.


Oplossing: de module Kowal_MsiStockFix

In plaats van de Magento-core aan te passen, maken we een plugin die na het aanmaken van de tabel:

  • utf8mb4_general_ci instelt
  • een index toevoegt op (is_salable, sku)

Plugin: IndexStructurePlugin.php

<?php
declare(strict_types=1);

namespace Kowal\MsiStockFix\Plugin;

use Magento\Framework\App\ResourceConnection;
use Magento\Framework\DB\Adapter\AdapterInterface;
use Magento\InventoryIndexer\Indexer\IndexStructure;
use Magento\InventoryMultiDimensionalIndexerApi\Model\IndexName;
use Magento\InventoryMultiDimensionalIndexerApi\Model\IndexNameResolverInterface;

class IndexStructurePlugin
{
    private const CHARSET = 'utf8mb4';
    private const COLLATION = 'utf8mb4_general_ci';

    public function __construct(
        private readonly ResourceConnection $resource,
        private readonly IndexNameResolverInterface $resolver
    ) {}

    public function afterCreate(IndexStructure $subject, $result, IndexName $indexName, string $connectionName)
    {
        $connection = $this->resource->getConnection($connectionName);
        $resolvedName = $this->resolver->resolveName($indexName);
        $table = $this->resource->getTableName($resolvedName);

        if (strpos($table, 'inventory_stock_') === false) {
            return $result;
        }

        if (!$connection->isTableExists($table)) {
            return $result;
        }

        $connection->query(
            sprintf(
                "ALTER TABLE `%s` CONVERT TO CHARACTER SET %s COLLATE %s",
                $table,
                self::CHARSET,
                self::COLLATION
            )
        );

        $indexes = $connection->getIndexList($table);
        if (!isset($indexes['IDX_STOCK_SALABLE_SKU'])) {
            $connection->addIndex(
                $table,
                'IDX_STOCK_SALABLE_SKU',
                ['is_salable', 'sku'],
                AdapterInterface::INDEX_TYPE_INDEX
            );
        }

        return $result;
    }
}

Installatie van de module

bin/magento module:enable Kowal_MsiStockFix
bin/magento setup:upgrade
bin/magento cache:flush
bin/magento indexer:reindex inventory

Effecten na implementatie

  • Collation verdwijnt niet meer
  • Snellere DELETEs tijdens de price index
  • Geen lock wait timeout meer
  • De inventory index rondt correct af
  • Minder join buffer en minder full scans

Samenvatting

Het probleem met de tabel inventory_stock_2 is niet direct zichtbaar, omdat alles er op het eerste gezicht "goed" uitziet en de indexen groen zijn.

Pas analyse van:

  • queryplannen
  • collation
  • het mechanisme dat MSI-tabellen opnieuw aanmaakt

laat de echte oorzaak zien.

Als je een webshop hebt met veel producten en MSI gebruikt, is een soortgelijke oplossing zeker de moeite waard.

Magento is vaak snel. Soms moet je het alleen een beetje helpen.

Previous Next