# Colonnes JSON MySQL 8 contre wp_postmeta : quel coût pour la synchro Algolia

> Deux façons de stocker des attributs produit complexes comparées sur leur effet réel sur le temps de synchronisation vers Algolia.

- Auteur : WordPress Développement
- Publié le : 2023-03-02
- Mis à jour le : 2023-03-02
- Catégorie : Performance
- URL : https://www.wpmoderne.fr/performance/colonnes-json-mysql8-vs-postmeta-algolia/

## L’essentiel

- Une colonne JSON dédiée réduit le nombre de jointures nécessaires à l'export
- wp_postmeta reste plus simple à interroger avec les filtres WordPress natifs
- Le gain dépend surtout de la largeur des attributs, pas de leur nombre

Faut-il vraiment continuer à empiler des lignes dans `wp_postmeta` quand un produit porte dix-huit attributs variables (matière, certification, dimensions, compatibilité, options de personnalisation) qu'il faut ensuite pousser vers un index Algolia à chaque modification ? Ce comparatif comptable, pas théorique, sur un catalogue de taille moyenne (environ 14 000 références), sur une boutique qui exportait ses attributs vers Algolia toutes les nuits via une tâche planifiée.

Deux architectures ont été mises en concurrence sur le même jeu de données : la structure classique `wp_postmeta`, une ligne par attribut, et une colonne JSON native ajoutée à une table personnalisée, exploitant les fonctions JSON de MySQL 8 (`JSON_EXTRACT`, `JSON_TABLE`, opérateur `->>`).

## La structure wp_postmeta telle qu'utilisée initialement

Chaque produit stockait ses dix-huit attributs sous forme de dix-huit lignes distinctes dans `wp_postmeta`, avec des clés comme `attr_matiere`, `attr_dimension_largeur`, `attr_certification_ce`. La récupération des attributs pour l'export Algolia passait par une requête du type :

```
SELECT meta_key, meta_value FROM wp_postmeta
WHERE post_id = %d AND meta_key LIKE 'attr_%'
```

Sur une table `wp_postmeta` qui dépassait les 4 millions de lignes toutes métadonnées confondues (miniatures, révisions, champs ACF, données de variation WooCommerce), cette requête restait indexée correctement sur `post_id`, mais le filtre `LIKE 'attr_%'` forçait un balayage de toutes les lignes du produit avant filtrage applicatif, sans compter la désérialisation PHP nécessaire pour les valeurs sérialisées.

## La structure alternative en colonne JSON

> L'essentiel à retenir : Une colonne JSON dédiée réduit le nombre de jointures nécessaires à l'export ; wp_postmeta reste plus simple à interroger avec les filtres WordPress natifs ; Le gain dépend surtout de la largeur des attributs, pas de leur nombre

La seconde architecture ajoutait une table dédiée, `wp_produit_attributs`, avec une colonne `attributs JSON` par produit, remplie une fois à la création puis mise à jour de façon atomique :

```
CREATE TABLE wp_produit_attributs (
    post_id BIGINT UNSIGNED PRIMARY KEY,
    attributs JSON NOT NULL
);

UPDATE wp_produit_attributs
SET attributs = JSON_SET(attributs, '$.certification_ce', 'oui')
WHERE post_id = 4821;
```

La lecture pour l'export se limitait alors à une seule ligne par produit, sans jointure ni filtre textuel :

```
SELECT post_id, attributs FROM wp_produit_attributs
WHERE post_id IN (4821, 4822, 4823);
```

## Le protocole de mesure

Le test a porté sur l'export complet des 14 000 références en amont de l'envoi vers Algolia (l'appel à l'API Algolia elle-même, identique dans les deux cas, a été exclu de la mesure pour isoler le seul coût de lecture MySQL) :

| Architecture | Temps de lecture pour 14 000 produits | Requêtes SQL émises |
| --- | --- | --- |
| wp_postmeta (18 lignes/produit) | 52 secondes | 14 000 |
| Colonne JSON dédiée | 22 secondes | 140 (par lots de 100) |

L'écart de 2,4 fois s'explique moins par la fonction JSON elle-même que par le changement de granularité : la table `wp_produit_attributs` permettait un chargement par lots de cent produits en une seule requête `IN()`, quand la structure `wp_postmeta` imposait une requête par produit pour conserver une logique de traitement simple côté PHP.

### Ce que la colonne JSON perd en confort

Le stockage JSON native a un coût invisible dans ce tableau : il sort du modèle de données que WordPress comprend nativement. Impossible d'utiliser `WP_Query` avec un `meta_query` pour filtrer les produits par attribut sans écrire du SQL brut ou une jointure manuelle. Toute interface d'administration listant les produits par certification, par exemple, a dû être réécrite avec des requêtes `$wpdb->prepare()` explicites plutôt que l'API de requêtes habituelle.

## Le verdict pour ce catalogue

La colonne JSON a été retenue, mais uniquement comme table miroir dédiée à l'export vers Algolia, alimentée par un hook `updated_postmeta` qui synchronise la structure JSON à chaque modification d'attribut fait via l'interface WordPress habituelle. La logique métier et l'administration continuent de s'appuyer sur `wp_postmeta`, familière aux équipes et compatible avec l'écosystème de plugins existant ; seule la couche d'export bénéficie de la structure optimisée.

- Pour un catalogue de moins de 2 000 références, l'écart de temps ne justifie probablement pas la complexité ajoutée d'une table miroir.
- Au-delà de 10 000 références avec des attributs nombreux, le gain devient significatif sur la durée totale d'un export nocturne.
- La cohérence entre les deux structures doit être garantie par un hook fiable, sous peine de désynchroniser l'index Algolia sans erreur visible.

> Une table miroir optimisée pour la lecture d'export vaut mieux qu'une réécriture complète du modèle de données existant.

## Pour aller plus loin

Ce comparatif ne tranche pas la question dans l'absolu : il documente un compromis technique adapté à un catalogue et une fréquence d'export donnés. Sur un catalogue avec beaucoup moins d'attributs par produit, l'écart mesuré serait probablement bien plus faible, et le passage à une colonne JSON perdrait une bonne partie de son intérêt face au coût de maintenance d'une structure parallèle.
