Start gratis proefperiode
Skip to content

Trage WooCommerce filters fixen — wp_postmeta database

Trage WooCommerce-filters zijn bijna altijd het gevolg van een database-architectuurprobleem. AJAX-plugins voeren bij elke klik zware SQL-JOINs uit — en het berekenen van facet-aantallen vermenigvuldigt de belasting. De definitieve oplossing is om te stoppen met het bevragen van de database tijdens het filteren: InstantFilter bouwt op de achtergrond een JSON-index op en filtert in plaats daarvan direct in de browser van de bezoeker.

Waarom zijn mijn WooCommerce-filters zo traag?

Ontwikkelaars die zoeken naar trage WooCommerce-filters oplossen of woocommerce filter admin-ajax.php traag merken meestal dat de initiële laadtijd (TTFB) van de categoriepagina prima is — maar dat er vervolgens vertragingen van 2 tot 3 seconden optreden na elke klik in de zijbalk. Redis, WP Rocket en grotere VPS-pakketten helpen nauwelijks, omdat de flessenhals niet de eerste weergave van de pagina is; het is wat er gebeurt nadat de gebruiker interactie heeft met de filters.

Open de Chrome DevTools → tabblad Netwerk, klik op een filter-checkbox en let op verzoeken naar admin-ajax.php of een aangepaste REST-route. Als elke klik een XHR-verzoek van 1 tot 5 seconden triggert, terwijl de initiële HTML in minder dan 500ms werd geladen, heb je te maken met een query-probleem tijdens runtime — niet met een paginacache-probleem. Dit is een cruciaal onderscheid: je kunt een trage query niet “weg-cachen” als de resultaten dynamisch moeten zijn voor elke unieke combinatie van filters.

Lees ons hoofdartikel over waar WooCommerce-filters je site vertragen voor het volledige plaatje. Het korte antwoord: traditionele plugins raadplegen WordPress en de database bij elke filterwijziging, wat een enorme overhead met zich meebrengt.

Waarom vertragen filters zelfs bij het gebruik van globale attributen?

Om te begrijpen waarom filteren inherent duur is in WordPress, moet je onder de motorkap kijken naar hoe WooCommerce data opslaat. WordPress is oorspronkelijk gebouwd als een blogplatform, niet als een e-commerce engine. Om WooCommerce te laten werken binnen het WordPress-ecosysteem, leunt het zwaar op het Entity-Attribute-Value (EAV) datamodel en geserialiseerde arrays. Hoewel dit flexibel is voor ontwikkelaars, is het een nachtmerrie voor database-performance op schaal.

WooCommerce splitst productattributen op over twee zeer verschillende opslagpatronen — en filter-plugins moeten beide bevragen bij elke AJAX-klik. Dit zorgt voor complexe queries die naarmate de shop groeit steeds trager worden.

Globale attributen (slug pa_color, etc.) slaan waarden op als taxonomietermen in wp_terms, gekoppeld via wp_term_relationships. Het filteren op “zwart” betekent het JOINen van deze tabellen voor elk product in de resultaatset. Bij een shop met duizenden producten en tientallen attributen, moeten er miljoenen rijen aan elkaar gekoppeld worden in het geheugen van de database-server.

Lokale attributen staan in één enkele geserialiseerde _product_attributes rij in wp_postmeta — dus niet één rij per attribuut. Alle definities van aangepaste attributen en de bijbehorende waarden (bijv. Katoen | Linnen) zitten in één PHP-geserialiseerde blob die MySQL niet direct kan indexeren of filteren. Voorbeeld van een opgeslagen waarde:

a:1:{s:9:"materiaal";a:6:{s:4:"name";s:9:"Materiaal";s:5:"value";s:15:"Katoen | Linnen";s:8:"position";i:0;s:10:"is_visible";i:1;s:12:"is_variation";i:0;s:11:"is_taxonomy";i:0;}}

Wanneer PHP die string leest, wordt het iets als materiaal → naam: Materiaal, waarde: Katoen | Linnen. De database kan niet filteren binnenin deze blob — PHP moet elke kandidaat-rij laden en “unserializen” om te zien of het aan de filtercriteria voldoet. Dit is extreem CPU-intensief en de reden waarom je serverbelasting ziet pieken zodra mensen filters gaan gebruiken.

Variaties slaan de gekozen waarde per variatie op in aparte meta-sleutels, bijv. attribute_pa_color = zwart. Variabele catalogi laten het aantal rijen snel exploderen: één ouderproduct met 20 maat/kleur-combinaties voegt 20 variatie-posts toe, elk met zijn eigen meta-sleutels. Een shop met 1.000 producten kan zo al snel 20.000 tot 50.000 rijen in de database hebben die doorzocht moeten worden.

Wanneer een shopper Merk + Kleur + Prijs selecteert, moet de filter-plugin taxonomietabel-JOINs, variatie-meta, prijs-meta en soms het unserializen van _product_attributes combineren. Een shop met 5.000 producten voelt dit al; bij 50.000 producten zit je in de miljoenen meta-rijen en term-koppelingen, wat elke klik pijnlijk traag maakt.

DatatypeLocatie in databaseFiltervriendelijk?
Globaal attribuut (pa_color)wp_terms + wp_term_relationshipsGedeeltelijk — veel JOINs nodig
Lokaal attribuut (custom)Geserialiseerde _product_attributes in wp_postmetaNee — moet door PHP verwerkt worden
Variatie-keuzeattribute_pa_* sleutels per variatie-rijGedeeltelijk — één meta-rij per variatie
Prijs / voorraad_price, _stock in wp_postmetaGedeeltelijk — numerieke meta-lookups

Waarom maken variabele producten WooCommerce-filters trager?

Simpele producten hebben één post-ID en een handvol meta-rijen. Variabele producten laten het aantal rijen in de database exploderen: elke variatie is een eigen wp_posts item (post_type product_variation) met een eigen prijs, voorraad, SKU en attribuut-meta. Dit is noodzakelijk voor de flexibiliteit van WooCommerce, maar het is een zware last voor de performance.

Filter-plugins die variaties als filterbare items tonen (of de voorraad per variatie tellen) moeten de ouderproducten JOINen met de kind-variaties. Een catalogus met 10.000 ouderproducten en gemiddeld 5 variaties per stuk betekent 50.000+ post-rijen nog voordat je aan attributen toekomt. Dit is de reden waarom B2B-shops en modewinkels met diepe variatie-structuren veel sneller tegen de grenzen van filter-performance aanlopen dan winkels die simpele producten verkopen.

Onze gids over het tonen van variaties als losse producten legt uit hoe het individueel indexeren van variaties zowel de gebruikerservaring (UX) als de query-belasting beïnvloedt — en waarom vooraf gebouwde indexen dit veel beter afhandelen dan live SQL-queries.

Wat is facet-counting en waarom is het dodelijk voor MySQL?

Het vinden van de juiste producten is slechts de helft van het werk. Een goede filter-interface toont bijgewerkte aantallen naast elke optie (bijv. “Rood (12)” nadat je Maat M hebt gekozen). Om deze getallen nauwkeurig te berekenen, zijn extra queries nodig — vaak één per filtergroep — bovenop de hoofdquery voor de producten. Dit proces noemen we “facet-counting”.

Stel je een zijbalk voor met Kleur (8 opties), Maat (6), Merk (15) en een Prijsrange. Nadat de shopper Maat M kiest, moet de plugin opnieuw tellen hoeveel producten er passen bij elke resterende Kleur, elk Merk en de prijsverdeling — terwijl de beperking “Maat M” behouden blijft. Dit kan betekenen dat er 30+ COUNT-queries worden uitgevoerd bij één enkele klik.

Met in totaal 30 opties in de zijbalk kan één klik tientallen SQL-statements triggeren. Dit is de reden waarom de CPU van admin-ajax.php piekt tijdens drukke periodes: het is niet de belasting van het afrekenen, maar het browsen door de catalogus dat de server platlegt. Ontdek hoe frontend-first filtering facet-queries volledig overbodig maakt door de aantallen in JavaScript te berekenen op basis van een vooraf gebouwde index.

Kunnen database-indexen trage WooCommerce-filters oplossen?

Een veelgehoord advies van hostingbedrijven is om “indexen toe te voegen aan je databasetabellen.” Aangepaste indexen op wp_postmeta(meta_key, meta_value) kunnen eenvoudige zoekopdrachten op sleutels iets versnellen — bijvoorbeeld het vinden van alle rijen waar meta_key = '_price'.

Ze kunnen echter niet helpen bij:

  • Filteren binnenin geserialiseerde _product_attributes blobs.
  • Taxonomie-JOINs over meerdere tabellen (wp_term_relationships en wp_terms).
  • Het herberekenen van facet-aantallen waarbij aparte queries per zijbalkgroep worden uitgevoerd.
  • De overhead van het opstarten van WordPress bij elk AJAX-verzoek.

Een JOIN over 5 tabellen met 2 miljoen rijen zal altijd traag blijven, ongeacht hoe goed deze is geïndexeerd. Je raakt simpelweg het architecturale plafond van WordPress. Hosting-support zal meer RAM voorstellen; dat bestrijdt de symptomen, maar lost de oorzaak niet op. Je gooit in feite geld naar een probleem dat met een slimmere aanpak gratis opgelost kan worden.

Als je catalogus groeit voorbij een paar duizend producten, plan dan een architectuurwijziging in plaats van weer een server-upgrade. Vergelijk de opties in onze vergelijking van de beste WooCommerce-filter-plugins.

Hoe diagnosticeer ik trage admin-ajax.php filterverzoeken?

Voordat je van plugin wisselt, is het verstandig om te bevestigen dat de flessenhals inderdaad de SQL-queries van de filters zijn — en niet iets anders op de pagina. Soms kunnen conflicten met andere plugins of een slecht geschreven thema ook voor vertraging zorgen.

  1. Installeer Query Monitor (alleen op een testomgeving!) en klik op een filter. Controleer hoeveel queries er draaien en wat de totale query-tijd is.
  2. Zoek naar herhaalde JOINs op wp_postmeta en wp_term_relationships.
  3. Vergelijk het aantal queries bij de eerste keer laden met het aantal na één filterklik — als klikken 20-50 queries toevoegen, is facet-counting waarschijnlijk de boosdoener.
  4. Schakel andere plugins tijdelijk uit om conflicten uit te sluiten; filter-plugins draaien zelden in isolatie en kunnen beïnvloed worden door SEO- of voorraad-plugins.
  5. Test met een kleinere subset van categorieën — als de vertraging meeschaalt met het aantal producten, is de architectuur de beperkende factor.

Als de query-tijd de AJAX-respons domineert, zullen caching-plugins zoals WP Rocket niet helpen — zij omzeilen de cache bij dynamische filter-URL’s om te voorkomen dat klanten verouderde resultaten zien. Zie waarom filters de WP Rocket-cache omzeilen.

Moet ik globale attributen gebruiken in plaats van lokale voor het filteren?

Het omzetten van producten van lokale (aangepaste) attributen naar globale attributen (pa_* taxonomieën) kan de filterpijn iets verminderen, omdat de waarden dan in geïndexeerde term-tabellen staan in plaats van in geserialiseerde blobs. Dit is een goede gewoonte voor nieuwe catalogi en helpt de database om de data iets sneller te vinden.

Het elimineert echter niet de AJAX-verzoeken of de facet-counting. Een globaal Kleur-filter vereist nog steeds JOINs met wp_term_relationships voor elk product. Bij 20.000+ producten met meerdere actieve filters stapelen deze JOINs zich nog steeds op — zeker in combinatie met variatie-meta en prijsranges. Het is een verbetering, maar geen oplossing voor het schaalbaarheidsprobleem.

Het migreren van duizenden bestaande producten van lokale naar globale attributen is bovendien een groot project met mogelijke gevolgen voor SEO en URL-structuren. Zie het opschonen van attributen als een aanvullende verbetering, niet als een vervanging voor het veranderen van de filter-architectuur bij grote webshops.

Hoe omzeilt InstantFilter wp_postmeta tijdens het filteren?

Het inzetten van meer serverbronnen (CPU/RAM) is een dure pleister op de wond. Om trage WooCommerce-filters echt op te lossen, moet je stoppen met het vragen aan de database om dingen te doen waarvoor hij niet is ontworpen. De database is fantastisch voor het opslaan en ophalen van individuele records, maar minder geschikt voor complexe, real-time facet-berekeningen op tienduizenden rijen tegelijk.

InstantFilter gebruikt WordPress en WooCommerce nog steeds om de index op te bouwen wanneer producten wijzigen — hierbij worden taxonomieën, variatie-meta en prijzen eenmalig uitgelezen tijdens de export. Maar tijdens het browsen filteren shoppers tegen een gecomprimeerd JSON-codebook in de browser. Dit betekent: nul AJAX-verzoeken, nul wp_postmeta JOINs per klik, en nul facet-count queries op de server.

De export-stap zet de rommelige opslag (globale attributen, lokale blobs, variatie-sleutels) om naar een platte, filter-klare structuur. Shoppers hoeven nooit te wachten tot MySQL geserialiseerde PHP-data parst bij elke klik op een checkbox. De browser doet dit werk in een fractie van een seconde, zonder dat de server er iets van merkt.

Omdat het filteren client-side draait, blijven je serverbronnen beschikbaar voor de processen die echt omzet genereren: het afrekenen, het bijwerken van het winkelmandje en webhooks. Het browsen door de catalogus concurreert niet langer met de orderverwerking om PHP-workers tijdens piektijden. Dit verhoogt de stabiliteit van je hele platform.

Installeer InstantFilter eerst op een staging-omgeving: kloon je catalogus, activeer de plugin op een zware categoriepagina en vergelijk de vertraging in het tabblad Netwerk met je huidige AJAX-plugin. Terugdraaien is eenvoudig als je een back-up hebt — maar de meeste webshops zien het verschil al bij de allereerste filterklik. De snelheidswinst is vaak spectaculair.

Voor grote catalogi hebben we filter-interacties gemeten van minder dan 5ms na de eerste download — bekijk hoe we 50.000+ producten direct filteren. Dit resultaat blijft stabiel, zelfs als de onderliggende WooCommerce-data een mix is van globale attributen, lokale geserialiseerde velden en honderden variaties per categorie.

Klaar om te testen? Start een proefperiode van 14 dagen op staging, vergelijk de filtervertraging in DevTools en ervaar het verschil. Voor een afweging van de architectuur ten opzichte van geïndexeerde AJAX-plugins, lees InstantFilter vs FacetWP voordat je een definitieve keuze maakt.

Documenteer de huidige vertraging van je filters voordat je overstapt — je zult deze cijfers nodig hebben wanneer stakeholders vragen of de migratie de moeite waard was. De data zal voor zich spreken.

Gerelateerde artikelen

Het oplossen van trage filters betekent het aanpassen van de architectuur — niet het kopen van meer CPU-kracht:

Veelgestelde vragen over trage WooCommerce-filters

WooCommerce gebruikt twee patronen. Globale attributen (pa_color, pa_size) worden opgeslagen als taxonomietermen in wp_terms en wp_term_relationships. Het filteren hiervan vereist JOINs over deze tabellen. Lokale (aangepaste) attributen staan geserialiseerd in één _product_attributes rij in wp_postmeta. Variatie-attributen staan ook in wp_postmeta als individuele rijen per variatie-post. AJAX-filters moeten al deze bronnen bij elke klik bevragen, en facet-counting herhaalt dat werk voor elke optie in de zijbalk.
Stop met het bevragen van de database bij elke filterklik. Opties zijn: geïndexeerde AJAX (snellere serverqueries) of frontend-first JSON-filtering (InstantFilter), waarbij het filteren naar de browser wordt verplaatst na een eenmalige download van het codebook. Voor catalogi boven de ~5.000 producten is frontend-first meestal de enige duurzame oplossing.
Ja. Nauwkeurige facet-aantallen vereisen extra SQL-queries per filtergroep. Een zijbalk met 30 opties kan tientallen queries per klik triggeren. InstantFilter berekent deze aantallen in JavaScript op basis van een vooraf gebouwde JSON-index — geen serverqueries meer nodig na de eerste download.
InstantFilter richt zich specifiek op het filteren van WooCommerce-producten met een focus op snelheid en SSR. FacetWP is uitstekend voor het filteren van verschillende post-types met geïndexeerde AJAX. Voor grote WooCommerce-catalogi waar AJAX-vertraging het grootste probleem is, is InstantFilter gebouwd als een high-performance alternatief.

Klaar om filteren instant te maken?

Start je 14-daagse proefperiode. 30 dagen geld-terug-garantie — op elk moment opzegbaar.