Magento 2 MSI y la tabla inventory_stock_2 – el asesino silencioso del rendimiento de indexación
En una de nuestras implementaciones recientes de Magento 2 (aprox. 200 000 productos, Multi Source Inventory activado) detectamos tiempos de indexación muy largos en los módulos:
- Inventory
- Product Price
- Category Products
Todos los índices clásicos parecían correctos. MariaDB tenía 256 GB de RAM, el buffer pool era razonable, los índices EAV estaban bien configurados y, aun así, seguían apareciendo consultas pesadas relacionadas con:
inventory_stock_2
Después de un análisis más profundo vimos que el problema no era el volumen de datos en sí, sino la forma en que Magento MSI crea la tabla inventory_stock_*.
¿De dónde sale la tabla inventory_stock_2?
La tabla inventory_stock_2 aparece en Magento después de activar
Multi Source Inventory (MSI).
Magento crea una tabla separada para cada stock:
- inventory_stock_1
- inventory_stock_2
- inventory_stock_3 ...
La tabla se crea dinámicamente mediante la clase:
Magento\InventoryIndexer\Indexer\IndexStructure
y tiene un aspecto similar a este:
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;
¿Dónde está el problema?
Hay dos problemas clave:
1. Falta de utf8mb4
La columna sku suele crearse con utf8_general_ci,
mientras que en catalog_product_entity tenemos:
utf8mb4_general_ci
Esto provoca:
- conversión de caracteres en los JOIN
- uso de join buffer
- planes de consulta peores
2. Falta de un índice que empiece por is_salable
Magento ejecuta con mucha frecuencia consultas del tipo:
WHERE stock_index.is_salable = 0
OR stock_index.is_salable IS NULL
Y como el único índice existente es:
(sku, quantity)
MariaDB no puede filtrar eficazmente por is_salable.
Resultado:
- escaneos completos de tabla
- DELETE largos durante el índice de precio
- lock wait timeout 1205
- inventory indexer en estado "processing" indefinidamente
¿Por qué no funciona un ALTER TABLE manual?
Porque Magento hace esto en cada indexación:
- DROP TABLE inventory_stock_X
- CREATE TABLE inventory_stock_X
Cualquier cambio manual desaparece.
Por eso la solución debe ser: modificar la tabla automáticamente después de crearla.
Solución: módulo Kowal_MsiStockFix
En lugar de modificar el core de Magento, creamos un plugin que, después de crear la tabla:
- establece utf8mb4_general_ci
- añade el índice (is_salable, sku)
Estructura del módulo
app/code/Kowal/MsiStockFix/
├── registration.php
├── etc/
│ ├── module.xml
│ └── di.xml
└── Plugin/
└── IndexStructurePlugin.php
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;
}
}
Instalación del módulo
bin/magento module:enable Kowal_MsiStockFix
bin/magento setup:upgrade
bin/magento cache:flush
bin/magento indexer:reindex inventory
Efectos después de la implementación
- La collation deja de desaparecer
- DELETE más rápidos en el índice de precio
- Sin lock wait timeout
- El índice de inventory finaliza correctamente
- Menos join buffer y menos full scans
Resumen
El problema con la tabla inventory_stock_2 no es obvio,
porque todo parece "correcto" y los índices se ven en verde.
Solo el análisis de:
- los planes de consulta
- la collation
- el mecanismo de recreación de tablas MSI
permite encontrar la causa real.
Si tienes una tienda con muchos productos y utilizas MSI, merece la pena aplicar una solución similar.
Magento a menudo es rápido. Solo hay que darle una pequeña ayuda.
