Peste 150 site-uri gestionate — administrare WordPress fără stres.

Optimizare Black Friday Woocommerce

Cum am optimizat un magazin WooCommerce pentru Black Friday schimbând o setare în LiteSpeed

În plin Black Friday(Da, Black Friday începuse deja. Calendarul încă nu fusese informat), un magazin WooCommerce avea toți cei 160 de workeri PHP blocați și pagini care se încărcau în 35–47 de secunde. După ajustarea decisivă a regulilor de purge din LiteSpeed Cache, workerii ocupați au scăzut la 17–30, iar paginile au coborât la ~2 secunde, fără hardware nou.

Problema

Comenzile veneau la 20–30 de secunde una de alta, iar serverul nu mai făcea față. Checkout-ul răspundea atât de greu încât clienții apăsau din nou pe butonul de comandă. Așa au apărut comenzi duplicate în „Plată în așteptare”: același client, aceeași sumă, de două sau trei ori.

Serverul: VPS într-un container cu 16 vCPU și 32 GB RAM, LiteSpeed cu lsphp 8.2, MariaDB, Redis ca object cache… ar trebui să zboare cu aceste resurse.

Diagnosticul pas cu pas

Din terminalul WHM, în timp ce magazinul primea comenzi. Am verificat raport litespeed, numar de procese, strace-uri, redis… Pe scurt: multe ferestre negre cu text alb și două creiere care începeau să aibă nevoie de răcire suplimentară.

Dap avem și AI să facem imagini...
Dap avem și AI să facem imagini…

Primele verificări arătau spre Redis. Redis sufocat: 160 de procese PHP care regenerau pagini ce ar fi trebuit să vină din cache.

Optimizările făcute înainte de soluția finală

Buffer pool MariaDB: de la 128 MiB la 4 GiB

Am mărit buffer pool-ul InnoDB din MariaDB de la 128 MiB la 4 GiB, pe un server cu 32 GiB RAM și o bază de date de aproximativ 8 GiB. Modificarea s-a aplicat dinamic, fără restart. În eșantioanele măsurate ulterior, citirile din fișierele de date InnoDB au scăzut de la aproximativ 2.000/s la 21/s, iar 99,93% dintre citirile logice au fost servite din buffer pool. Comparația reflectă și traficul și încălzirea cache-ului din acele intervale.

Optimizarea a îmbunătățit eficiența bazei de date, dar nu a eliminat blocajul: toți cei 160 de workeri PHP rămâneau ocupați, cu aproximativ 187–221 de cereri la coadă. MariaDB respira mai ușor. Noi încă nu.

Plugin-uri, cron și restartul PHP

Am redus și activitatea auxiliară: am dezactivat plugin-uri neesențiale și am înlocuit apelul HTTP al cron-ului cu execuție locală prin PHP 8.2, la fiecare cinci minute, cu flock pentru prevenirea suprapunerilor(lipsa acesteia devenise o problema între timp). Aceste intervenții nu au golit coada PHP. Existența unor joburi de sincronizare restante nu dovedea că un singur plugin ocupa toți workerii.

Restartul workerilor PHP a oferit doar o ameliorare temporară: Pentru câteva clipe am avut senzația că am rezolvat. Apoi s-au întors toți cei 160 și coada de după grrr…

Două fundături

Oprirea object cache-ului

Cu object cache-ul oprit, mai multă muncă a ajuns în MariaDB. Inițial, debitul PHP a crescut, dar CPU-ul a ajuns aproape de 100%, au apărut răspunsuri HTTP 500, iar serverul a repornit. Am încheiat proba și am reactivat Redis.

Renice

Bla bla …. alti termeni tehnici … Sistemul nu permite să crești prioritatea lui Redis, așa că am scăzut-o pe a proceselor PHP cu renice -n 10. Redis a coborât de la 38 ms la 5–9 ms, iar cererile PHP terminate s-au dublat. Odată deblocat Redis, însă, procesorul a ajuns la 6–10% liber, iar load-ul a urcat la 62. O pază pusă dinainte a făcut automat graceful restart la LiteSpeed – pentru a nu ajunge iar la un reboot…

Concluzia: problema era că prea multe cereri ajungeau la PHP, nu ordinea în care primeau procesorul.

Cauza reală: cache-ul golit la fiecare comandă

Multe vânzări înseamnă coș, checkout și pagină de mulțumire, care nu se pot pune în cache. Am împărțit ~1 570 de cereri dinamice din log-ul de acces:

Tip de cerereProcentSe poate servi din cache?
Prima pagină, categorii, produse58%Da
Coș, checkout, plată, mulțumire18%Nu
get_refreshed_fragments10%Se poate reduce
Admin, AJAX, API9%Parțial
Căutare4%Nu

Cererile către coș, checkout, plată și pagina de mulțumire reprezentau aproximativ 18% din numărul cererilor dinamice analizate. Cele 58% către prima pagină, categorii și produse arătau însă un potențial important de reducere a muncii PHP prin cache pentru vizitatorii anonimi eligibili. Procentele sunt rotunjite.

În LiteSpeed Cache, la orice actualizare de produs, inclusiv o schimbare de stoc după o comandă, erau bifate pentru golire automată toate paginile, prima pagină, pagina de articole, toate categoriile și arhivele. Cu o comandă la 20–30 de secunde, cache-ul era golit încontinuu, iar cererile care ratau cache-ul ajungeau să dureze 35–47 s, inclusiv așteptarea la coadă. Aceași pagină servită din cache fusese măsurată la 0,018 s.

O setare...
O setare !?! Btw, wow cu cate mâini lucram

Soluția: era simplă setările schimbate în LiteSpeed Cache

Sigur că era o bifă. După ce o găsești, orice problemă pare simplă. În cele din urmă era o setare simplă cum de nu ne-am gândit la ea de la început… wp-admin → LiteSpeed Cache → Cache.

  1. Tab-ul Purge → Auto Purge Rules For Publish/Update: debifate All pages, Front page, Home page, Pages, Author archive, Post type archive, Yearly, Monthly și Daily archive, Term archive. „Serve Stale” a rămas pornit.
  2. Tab-ul WooCommerce → Product Update Interval: „Purge product only when the stock status changes. Do not purge categories on changes to the quantity or stock status.”

Compromisul: prima pagină și categoriile pot afișa temporar informații de stoc vechi. TTL-ul de 30 de minute nu garantează actualizarea afișării în cel mult 30 de minute: Opțiunea aleasă pentru WooCommerce invalidează pagina produsului la schimbarea statutului de stoc, nu la fiecare modificare de cantitate; cantitatea exactă afișată poate rămâne veche. Checkout-ul rămâne dinamic și verifică disponibilitatea la plasarea comenzii.

Rezultate

Măsurători la 1–2 minute după salvare, cu trafic mai mare decât înainte (88–163 de cereri/s față de 61–76). Comparația surprinde etapa finală a intervenției, după optimizările anterioare; în fazele precedente, coada PHP ajunsese și la peste 200 de cereri. Timpii paginilor de mai jos sunt timpi de răspuns și includ eventuala așteptare la coadă.

IndicatorÎnainteDupă
Workeri PHP ocupați (din 160)84–16017–30
Cereri PHP la coadăpână la 610
Cereri PHP terminate pe secundă4–69–12
Timp de răspuns Redis34–38 ms0,33 ms
Prima pagină35–47 s2,2 s
Categoria Black Fridaypeste 40 s2,3 s
Checkout5 s0,47 s
Load (1 min)10–12, vârfuri de 62~11, stabil

Cu paginile servite din nou rapid, numărul de comenzi intrate a crescut vizibil imediat după schimbare.

Redis s-a vindecat singur: cu ~25 de procese PHP active în loc de 160, nu mai stătea la coadă după procesor. Lecția: când un server WooCommerce cade sub comenzi, întâi verifică dacă paginile care pot fi servite din cache chiar sunt servite din cache.

Magazinul tău WooCommerce încetinește când vin comenzile?

*Atenție! Setările de mai sus au funcționat pe acest server și pot diferi de la site la site. Înainte de orice modificare, asigură-te că ai un backup valid și nu apăsa „Purge All” în plin vârf de trafic.

Secret Link