Un client gérant un catalogue de pièces automobiles stockait, pour chaque produit, un ensemble de caractéristiques techniques structurées : dimensions, compatibilités moteur, certifications, sous forme d’un tableau PHP sérialisé dans un unique champ wp_postmeta. Cette approche fonctionne, mais interroger une caractéristique précise, par exemple tous les produits compatibles avec un moteur donné, demandait de charger et désérialiser ce champ pour chaque produit, en PHP, faute de pouvoir filtrer efficacement une valeur sérialisée directement en SQL.
MySQL 8 propose un type de colonne JSON natif, avec des fonctions permettant d’interroger directement une clé à l’intérieur du document stocké, sans désérialisation applicative. Ce comparatif mesure si ce type de colonne apporte un gain réel sur ce cas d’usage précis, par rapport à l’approche wp_postmeta classique.
Le protocole de test
Une table de 100 000 produits fictifs a été générée deux fois : une fois avec les caractéristiques techniques stockées en wp_postmeta sérialisé classique, une fois dans une table personnalisée avec une colonne caracteristiques de type JSON natif MySQL 8. Les deux approches ont été testées sur la même requête métier : trouver tous les produits compatibles avec un moteur « 1.6 HDi », une valeur enfouie dans la structure de données de chaque produit.
La requête côté wp_postmeta classique
Sans possibilité de filtrer directement une clé sérialisée en SQL, la seule approche viable consistait à charger l’ensemble des 100 000 lignes candidates, puis à désérialiser et filtrer côté PHP :
$resultats = array();
$tous = $wpdb->get_results( "SELECT post_id, meta_value FROM wp_postmeta WHERE meta_key = 'caracteristiques'" );
foreach ( $tous as $ligne ) {
$donnees = maybe_unserialize( $ligne->meta_value );
if ( in_array( '1.6 HDi', $donnees['moteurs_compatibles'] ?? array(), true ) ) {
$resultats[] = $ligne->post_id;
}
}
Temps mesuré pour cette requête sur les 100 000 lignes : 3,4 secondes en moyenne, l’essentiel du coût provenant de la désérialisation PHP répétée plutôt que de la lecture SQL elle-même.

La requête côté colonne JSON native
Avec une colonne JSON native, la fonction JSON_CONTAINS() de MySQL permet de filtrer directement en SQL, sans jamais charger les lignes non concernées côté PHP :
SELECT id FROM produits_caracteristiques
WHERE JSON_CONTAINS(caracteristiques, '"1.6 HDi"', '$.moteurs_compatibles');
Temps mesuré pour cette requête équivalente : 420 millisecondes en moyenne, un gain net d’un facteur proche de huit par rapport à l’approche wp_postmeta. Un index fonctionnel généré à partir du chemin JSON a permis de réduire encore ce temps à 90 millisecondes, une option qui n’a pas d’équivalent simple avec des données sérialisées en wp_postmeta.
ALTER TABLE produits_caracteristiques
ADD INDEX idx_moteurs ((CAST(caracteristiques->>'$.moteurs_compatibles' AS CHAR(255)) COLLATE utf8mb4_bin));
Ce que ce gain ne dit pas de tout
Ce comparatif porte spécifiquement sur une requête de filtrage sur une valeur imbriquée. Pour un usage plus simple, comme la simple lecture de toutes les caractéristiques d’un produit déjà connu par son identifiant, l’écart entre les deux approches devient négligeable, wp_postmeta restant tout à fait adapté et surtout mieux intégré nativement à l’écosystème d’extensions WordPress qui s’attendent à cette structure de table.
| Scénario | wp_postmeta sérialisé | Colonne JSON native |
|---|---|---|
| Lecture d’un produit par ID | 2 ms | 2 ms |
| Filtrage sur une clé imbriquée, sans index | 3,4 s | 420 ms |
| Filtrage sur une clé imbriquée, avec index fonctionnel | non applicable | 90 ms |
Ce qu’il faut peser avant de migrer
- Une table personnalisée avec colonne JSON sort du modèle standard WordPress, ce qui complique l’intégration avec certaines extensions tierces.
- Le gain n’est significatif que sur des requêtes de filtrage complexes sur des données imbriquées, pas sur une lecture simple par identifiant.
- Un index fonctionnel sur une colonne JSON demande une maintenance et une compréhension technique supplémentaires pour l’équipe.
Notre verdict
Les colonnes JSON natives de MySQL 8 apportent un gain réel et mesurable sur des requêtes de filtrage complexes portant sur des données structurées imbriquées, au prix d’une sortie du modèle wp_postmeta standard. Ce choix se justifie surtout pour des tables personnalisées à fort volume et à requêtes de filtrage fréquentes, pas comme un remplacement systématique du système de métadonnées natif de WordPress.