Cas d'èxit · Retail B2B

2.942 consultes per fitxa de producte. Ara 199.

Com un retailer B2B multibotiga va baixar l'Speed Index de la seva fitxa de producte de 7 segons a menys d'un: primer la base de dades, després la memòria cau i, al final, el frontend.

Client
Retailer B2B, més de 20 M€ l'any
Plataforma
Magento 2 · 8 botigues
Catàleg
Més de 20.000 SKU
Focus
Fitxa de producte
Abast
Backend, Varnish, frontend, Hyvä
Consultes SQL · fitxa
2,942199
−93%
Temps SQL · fitxa
719ms98ms
−87%
LCP · usuaris reals, escriptori
4.1s1.7s
−59% · Bo
Pes de pàgina · escriptori
4.4MB496KB
−89% · Hyvä, preproducció
El problema

Una fitxa de producte, construïda com vint.

Un retailer B2B que ven més de 20 M€ l'any té vuit botigues en una sola instal·lació de Magento. Les seves fitxes de producte són molt completes: estoc per magatzem, accessoris, paquets, documents, vídeos. També s'havien tornat lentes. Sense memòria cau, una fitxa trigava gairebé 10 segons de servidor, llançava 2.942 consultes SQL i carregava sencers uns 20 productes per mostrar-ne un.

Les dades d'usuaris reals ho confirmaven. A escriptori, l'LCP del percentil 75 era de 4,1 s i el temps fins al primer byte, de 3,5 s. Google Search Console no parava de marcar URLs lentes. Als pics de trànsit, una consulta pesada provocava deadlocks a la base de dades.

Core Web Vitals, usuaris reals, abansCrUX, escriptori, percentil 75
4.1s
LCP (bo ≤ 2,5 s)
3.5s
TTFB (bo ≤ 0,8 s)
0.12
CLS (bo ≤ 0,1)
Suspès
Avaluació Core Web Vitals
Capítol 1 · Base de dades i PHP

Primer el servidor. La memòria cau, després.

La memòria cau amaga una pàgina lenta, però cada fallada de memòria cau, cada purga i cada URL nova ho continuen pagant. Vam perfilar fitxes sense memòria cau amb Blackfire i vam revisar les estadístiques de consultes de la mateixa base de dades. Després vam arreglar cada bloc de la pàgina, un per un.

  • Estoc: una consulta en lloc de centenars.L'estoc es llegia amb un SELECT per cada origen d'inventari i per cada producte. Amb 20 orígens i 30 productes en una pàgina, són 600 consultes. Ara una sola consulta amb JOIN retorna l'estoc de tots els productes i distingeix el magatzem propi dels proveïdors en dropshipping.
  • Una consulta pesada d'una extensió, a la memòria cau.Una extensió d'adjunts de producte llançava una consulta tan lenta que provocava deadlocks als pics de trànsit. Vam reescriure la consulta i vam desar el resultat a Redis.
  • Taules planes per a les relacions de producte.Quatre blocs de productes relacionats i un de paquets carregaven models de producte complets. Ara dues taules d'índex planes (productes simples i paquets) guarden el que mostren aquests blocs: preu, estoc, URL i imatge. Una taula de rànquing de vendes permet ordenar i limitar en SQL. Reindexar triga menys d'un minut per als simples i dos per als paquets, amb més de 20.000 productes i 8 botigues.
  • Sense recarregar dades que la pàgina ja té.Les dades estructurades (rich snippets) tornaven a carregar productes complets per mostrar valors que ja eren a la pàgina. Ara llegeixen els objectes ja construïts, o Redis.
-- before: one query per source, per product, per block (20 × 30 = 600)
SELECT quantity, status FROM inventory_source_item
WHERE sku = ? AND source_code = ?;

-- after: one query per page (simplified)
SELECT si.sku, src.is_dropship, SUM(si.quantity) AS qty
FROM inventory_source_item si
JOIN inventory_source_stock_link l ON l.source_code = si.source_code AND l.stock_id = ?
JOIN inventory_source src ON src.source_code = si.source_code
WHERE si.sku IN (…) AND si.status = 1
GROUP BY si.sku, src.is_dropship;
Fitxa sense memòria cau, després com a % d'abansPerfils de Blackfire
AbansDesprés
Temps total
100%
43%
Espera d'E/S
100%
41%
CPU
100%
46%
Pic de memòria
100%
86%
Xarxa
100%
52%
Temps SQL
100%
14%
Consultes SQL
100%
7%
Fitxa de producte sense memòria cauPerfils de Blackfire
MètricaAbansDesprésCanvi
Temps total9.76s4.23s−56%
Espera d'E/S5.66s2.33s−58%
CPU4.1s1.9s−53%
Pic de memòria292MB250MB−14%
Xarxa15.0MB7.74MB−48%
Temps SQL719ms98ms−87%
Consultes SQL2,942199−93%
Capítol 2 · Varnish i preescalfament de memòria cau

Cada pàgina a la memòria cau abans que el primer visitant la demani.

varnishstat va mostrar que als pics de trànsit Varnish es quedava sense memòria i expulsava pàgines de la memòria cau per fer lloc a noves. Li vam donar prou memòria per a tot el catàleg. Després ens vam assegurar que la memòria cau estigués sempre plena.

Un petit mòdul de Magento exporta totes les URLs de CMS, categories i productes de cada botiga. Un preescalfador de memòria cau escrit en Rust les demana totes amb un límit fix de peticions simultànies, per no sobrecarregar mai els servidors. Magento separa la memòria cau segons la galeta X-Magento-Vary, així que el preescalfador recorre cada URL un cop per grup de clients. La memòria cau es purga cada nit i es torna a escalfar just després. Durant el dia, el preescalfador continua funcionant.

// warmer core: bounded concurrency over the exported URL list
stream::iter(urls)
    .map(|url| client.get(url).header(COOKIE, &vary_cookie).send())
    .buffer_unordered(MAX_IN_FLIGHT)  // never more than N requests at once
    .for_each(|res| async { record(res) })
    .await;
Preescalfador de memòria cauPer botiga
~15 min
Recorregut complet, memòria cau buida
1–2 min
Mateix recorregut, memòria cau calenta
2
Grups de clients recorreguts
8
Botigues
Capítol 3 · Frontend i Magepack

De 175 peticions a 58.

La pàgina feia 175 peticions i pesava 4,4 MB. Diversos blocs es carregaven per AJAX després de la mateixa pàgina, així que aquesta continuava canviant durant segons. L'Speed Index era de 7 s i el CLS de 0,65. Massa fonts, massa RequireJS i un script d'A/B testing que esvaïa la pàgina quan ja s'havia carregat.

  • Els blocs que es carregaven per AJAX després de la pàgina ara vénen a l'HTML de la memòria cau.Això va resoldre la major part de l'Speed Index i dels salts de layout.
  • JavaScript empaquetat amb Magepack.Abans vam provar el bundling de Magento i una altra extensió. Magepack va donar el millor resultat.
  • La imatge principal carrega primer.La primera imatge de la galeria carrega sense lazy loading, amb fetchpriority="high".
  • Els formularis del CRM es carreguen sota demanda.L'script de formularis només es carrega quan el visitant obre un formulari.
  • Els vídeos mostren una miniatura fins que s'hi fa clic.Els iframes de vídeo només es carreguen després d'un clic, a les descripcions i a la galeria.

Les etiquetes de màrqueting (A/B testing, gestor d'etiquetes, consentiment) són d'un altre equip i s'estan corregint per separat. Vam mesurar amb i sense elles perquè cada equip veiés la seva part.

Capítol 4 · Hyvä

RequireJS era el sostre. Hyvä el va treure.

Magepack va portar l'escriptori fins on RequireJS i Knockout permetien. Al mòbil continuàvem al voltant de 50. La fitxa de producte ja està refeta amb Hyvä i Alpine.js i és en proves.

  • El menú complet es desa a Redis. El seu nivell més profund es carrega en interactuar.
  • Tot el JavaScript, galeria inclosa, espera la primera interacció. La imatge LCP es precarrega al <head>.
  • Els modals es renderitzen en interactuar, no en carregar.
  • Imatges recodificades, icones SVG alleugerides i les icones repetides servides des d'un únic sprite SVG.
  • La font web es limita al subconjunt llatí: de 80 KB a 30 KB.
Fitxa de producte, escriptori, tres etapesWebPageTest · 5/1 Mbps, 28 ms
TTFB
183ms−24%
Inici de render
0.32s−81%
FCP
0.52s−73%
LCP
0.67s−82%
Speed Index
0.68s−90%
CLS
0.00−100%
TBT
48ms−82%
Càrrega completa
1.02s−93%
Peticions
35−80%
Pes de pàgina
496KB−89%
Bloquejants
1−97%
Puntuació de rendiment a PageSpeed Insights, fitxa de producteDades de laboratori · Lighthouse
67Escriptori · abansMillorable
82Escriptori · MagepackMillorable
95Sense etiquetes de màrquetingBo
100Escriptori · HyväBo
53Mòbil · MagepackMillorable
86Mòbil · HyväMillorable

Les xifres d'abans i de Magepack són mesures en producció. Les d'Hyvä vénen de l'entorn de preproducció sense etiquetes de tercers i se substituiran per mesures en producció després del llançament.

Usuaris reals, avui

Les dades de laboratori són una promesa. Les d'usuaris reals, la prova.

Google posiciona segons el que viuen els visitants reals, mesurat per Chrome durant 28 dies. A escriptori, LCP, FCP i INP ja aproven, i el temps fins al primer byte és tres vegades més ràpid que abans.

Core Web Vitals, usuaris reals, abans → araCrUX, escriptori, percentil 75
LCP
4.1s1.7s
−59% · Bo
FCP
3.8s1.5s
−61% · Bo
INP
119ms100ms
−16% · Bo
TTFB
3.5s1.1s
−69%
CLS
0.120.11
A millorar · vegeu a sota

El CLS és l'única mètrica que encara supera el llindar. Un script de màrqueting afegit després de l'optimització desplaça el layout, i s'està corregint en el llançament d'Hyvä. Per això el rendiment necessita monitoratge: un sol script nou pot desfer mesos de feina.

Com es va fer

Quatre fases, cadascuna mesurada.

  1. Fase 1Profiling amb Blackfire i estadístiques de consultes de RDS. Estoc, adjunts, taules planes, dades estructurades.
  2. Fase 2Memòria de Varnish dimensionada per als pics. Exportació d'URLs i preescalfador de memòria cau en producció.
  3. Fase 3Blocs AJAX eliminats, Magepack, imatge LCP, scripts de tercers sota demanda.
  4. Fase 4Tema Hyvä, començant per la fitxa de producte. Ara en proves.
Què va canviar per al negoci
  • Avisos d'URLs lentes a Google Search Console reduïts en més d'un 80%.
  • LCP d'usuaris reals a escriptori de 4,1 s a 1,7 s.
  • S'han acabat els deadlocks de base de dades als pics de trànsit.
  • Pàgines servides des de Varnish, en calent, a totes hores.
  • Puntuació PageSpeed a escriptori de 67 a 95 sense etiquetes de tercers, i 100 amb Hyvä.

Vols xifres així? Comença amb una auditoria.