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.
