Magento 2 MSI e la tabella inventory_stock_2 – il killer silenzioso delle prestazioni di indicizzazione
In una delle nostre recenti implementazioni Magento 2 (circa 200.000 prodotti, Multi Source Inventory abilitato) abbiamo notato tempi di indicizzazione molto lunghi nei moduli:
- Inventory
- Product Price
- Category Products
Tutti gli indici classici sembravano corretti. MariaDB aveva 256 GB di RAM, il buffer pool era adeguato, gli indici EAV erano presenti eppure continuavano a comparire query pesanti legate a:
inventory_stock_2
Dopo un’analisi più approfondita si è scoperto che il problema non era la quantità di dati in sé, ma il modo in cui Magento MSI crea la tabella inventory_stock_*.
Da dove arriva la tabella inventory_stock_2?
La tabella inventory_stock_2 compare in Magento dopo l’attivazione di
Multi Source Inventory (MSI).
Magento crea una tabella separata per ogni stock:
- inventory_stock_1
- inventory_stock_2
- inventory_stock_3 ...
La tabella viene creata dinamicamente dalla classe:
Magento\InventoryIndexer\Indexer\IndexStructure
e ha più o meno questa struttura:
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;
Dov’è il problema?
Ci sono due problemi chiave:
1. Mancanza di utf8mb4
La colonna sku viene spesso creata in utf8_general_ci,
mentre in catalog_product_entity troviamo:
utf8mb4_general_ci
Questo provoca:
- conversione di caratteri durante i JOIN
- uso del join buffer
- piani di query peggiori
2. Nessun indice che inizi con is_salable
Magento esegue molto spesso query del tipo:
WHERE stock_index.is_salable = 0
OR stock_index.is_salable IS NULL
E poiché l’unico indice è:
(sku, quantity)
MariaDB non riesce a filtrare in modo efficiente per is_salable.
Risultato:
- full table scan
- DELETE molto lunghi durante il price index
- lock wait timeout 1205
- inventory indexer in "processing" senza fine
Perché un ALTER TABLE manuale non funziona?
Perché Magento ad ogni indicizzazione esegue:
- DROP TABLE inventory_stock_X
- CREATE TABLE inventory_stock_X
Ogni modifica manuale scompare.
Per questo la soluzione deve essere: modificare automaticamente la tabella dopo la sua creazione.
Soluzione: modulo Kowal_MsiStockFix
Invece di modificare il core di Magento, creiamo un plugin che, dopo la creazione della tabella:
- imposta utf8mb4_general_ci
- aggiunge un indice (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;
}
}
Installazione del modulo
bin/magento module:enable Kowal_MsiStockFix
bin/magento setup:upgrade
bin/magento cache:flush
bin/magento indexer:reindex inventory
Effetti dopo l’implementazione
- La collation non scompare più
- DELETE più rapidi nel price index
- Niente più lock wait timeout
- L’inventory index termina correttamente
- Meno join buffer e meno full scan
Riepilogo
Il problema con la tabella inventory_stock_2 non è immediato da vedere,
perché tutto sembra "corretto" e gli indici risultano verdi.
Solo l’analisi di:
- query plan
- collation
- meccanismo di ricreazione delle tabelle MSI
permette di trovare la vera causa del problema.
Se hai uno store con molti prodotti e usi MSI, vale la pena implementare una soluzione simile.
Magento spesso è veloce. Bisogna solo dargli una piccola mano.
