Magento 2 MSI und die Tabelle inventory_stock_2 – der stille Killer der Indexierungsleistung
In einem unserer letzten Magento-2-Projekte (ca. 200.000 Produkte, Multi Source Inventory aktiviert) haben wir sehr lange Indexierungszeiten in den Modulen festgestellt:
- Inventory
- Product Price
- Category Products
Alle klassischen Indizes sahen korrekt aus. MariaDB hatte 256 GB RAM, der Buffer Pool war sinnvoll dimensioniert, die EAV-Indizes waren vorhanden, und trotzdem tauchten weiterhin schwere Queries im Zusammenhang mit
inventory_stock_2
auf. Nach einer tieferen Analyse stellte sich heraus, dass nicht die Datenmenge selbst das Problem war, sondern die Art und Weise, wie Magento MSI die Tabelle inventory_stock_* erstellt.
Woher kommt die Tabelle inventory_stock_2?
Die Tabelle inventory_stock_2 erscheint in Magento nach der Aktivierung von
Multi Source Inventory (MSI).
Magento erstellt für jeden Stock eine separate Tabelle:
- inventory_stock_1
- inventory_stock_2
- inventory_stock_3 ...
Die Tabelle wird dynamisch von der Klasse
Magento\InventoryIndexer\Indexer\IndexStructure
erstellt und sieht ungefähr so aus:
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;
Wo liegt das Problem?
Es gibt zwei zentrale Probleme:
1. Fehlendes utf8mb4
Die Spalte sku wird häufig mit utf8_general_ci erstellt,
während wir in catalog_product_entity folgendes haben:
utf8mb4_general_ci
Das verursacht:
- Zeichenkonvertierung bei JOINs
- Verwendung von Join Buffer
- schlechtere Query-Pläne
2. Kein Index, der mit is_salable beginnt
Magento führt sehr häufig Abfragen wie diese aus:
WHERE stock_index.is_salable = 0
OR stock_index.is_salable IS NULL
Da der einzige Index jedoch
(sku, quantity)
ist, kann MariaDB nicht effizient nach is_salable filtern.
Ergebnis:
- vollständige Tabellenscans
- lange DELETEs beim Price Index
- lock wait timeout 1205
- Inventory Indexer bleibt endlos auf "processing"
Warum funktioniert ein manuelles ALTER TABLE nicht?
Weil Magento bei jeder Indexierung Folgendes macht:
- DROP TABLE inventory_stock_X
- CREATE TABLE inventory_stock_X
Jede manuelle Änderung verschwindet also wieder.
Deshalb muss die Lösung so aussehen: automatische Anpassung der Tabelle direkt nach ihrer Erstellung.
Lösung: das Modul Kowal_MsiStockFix
Anstatt den Magento-Core zu verändern, erstellen wir ein Plugin, das nach der Erstellung der Tabelle:
- utf8mb4_general_ci setzt
- einen Index (is_salable, sku) hinzufügt
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;
}
}
Installation des Moduls
bin/magento module:enable Kowal_MsiStockFix
bin/magento setup:upgrade
bin/magento cache:flush
bin/magento indexer:reindex inventory
Effekte nach der Implementierung
- Die Collation verschwindet nicht mehr
- Schnellere DELETEs beim Price Index
- Kein lock wait timeout mehr
- Der Inventory Index läuft sauber durch
- Weniger Join Buffer und weniger Full Scans
Zusammenfassung
Das Problem mit der Tabelle inventory_stock_2 ist wenig offensichtlich,
weil zunächst alles "korrekt" aussieht und die Indizes grün sind.
Erst die Analyse von
- Query-Plänen
- Collation
- dem Mechanismus zur Neuerstellung von MSI-Tabellen
führt zur eigentlichen Ursache.
Wenn Sie einen Shop mit sehr vielen Produkten betreiben und MSI nutzen, lohnt sich eine ähnliche Lösung auf jeden Fall.
Magento ist oft schnell. Man muss ihm nur manchmal ein wenig helfen.
