...

PHP Session Garbage Collection: Hvorfor det kan blokere din hjemmeside

php session gc kan blokere forespørgsler, fordi den binder PHP-processen i lang tid, når den rydder op i titusindvis af sessionsfiler, hvilket får andre forespørgsler til at vente. Jeg viser, hvordan probabilistisk oprydning, filspærringer og langsom I/O fører til mærkbare forsinkelser, og hvordan jeg undgår disse forsinkelser med klare indstillinger, cron-jobs og RAM-lagerplads, så Websted forbliver flydende.

Centrale punkter

  • årsag til problemet: Probabilistisk GC, fil-I/O og låse medfører ventetider.
  • risikofaktor: Mange sessioner (f.eks. 170.000) forlænger hver GC-kørsel.
  • WordPress: Admin + Heartbeat forværrer forsinkelser.
  • Hosting: RAM, SSD og isolering reducerer GC-omkostninger.
  • Løsning: Cron-oprydning og Redis fremskynder anmodninger.

PHP Session Garbage Collection kort forklaret

Sessions gemmer statusdata mellem anmodninger, oftest som filer i filsystem. Garbage Collection fjerner forældede filer, hvis ændringstid er ældre end session.gc_maxlifetime, ofte 1440 sekunder. Som standard starter PHP denne oprydning probabilistisk via session.gc_probability og session.gc_divisor, ofte som 1 ud af 1000 opkald. Det lyder harmløst, men ved stor trafik rammer det konstant nogen, der må udholde hele forløbet. Jo flere filer der ligger i sessionsmappen, jo længere blokerer Oprydning processen.

Hvorfor blokerer oprydningen for anmodninger?

En GC-kørsel skal liste session-mappen, kontrollere hver fil og slette gamle poster, hvilket hurtigt kan blive meget tidskrævende på langsomme I/O'er. Sekunder koster. Hvis der er 170.000 filer, kører mange systemkald efter hinanden, hvilket belaster CPU, RAM og lagerplads. PHP-processer, der startes parallelt, forsøger undertiden at slette samtidigt og forårsager yderligere fillåse. Dette forlænger ventetiden, fordi processerne bremser eller blokerer hinanden. Hvis man går dybere ind i Session-låsning går ind, ser man, hvor stærkt Locking præger responstidprofilen og øger Time-to-First-Byte, især ved belastningsspidser, som jeg vil undgå ved at bruge GC afkoble.

WordPress: langsomme administrationssider på grund af sessioner

Admin-området kræver mere CPU og databaseadgang end frontend, hvilket gør enhver ekstra forsinkelse mærkbar. gør. Hvis garbage collection starter netop på det tidspunkt, øges tiden til den færdige HTML-udskrift betydeligt. Heartbeat-API'en forespørger desuden serveren og kolliderer i uheldige tilfælde med en GC-kørsel. Det gør, at backend føles langsom, og klik tager længere tid, selvom den egentlige logik ikke gør meget. Jeg afhjælper dette ved at sætte sandsynligheden for GC i forespørgsler til nul og oprydningsarbejde kan planlægges uden for svartiderne.

Hosting-ydelse og infrastruktur

På delte systemer deler mange projekter I/O-kapacitet, hvilket betyder, at en enkelt GC-kørsel påvirker andre websteder. Bremser. Bedre hardware med hurtig NVMe-lagring og tilstrækkelig RAM reducerer omkostningerne pr. filadgang. En ren isolering pr. kunde eller container forhindrer, at fremmede belastningsspidser påvirker dit projekt. Jeg kontrollerer også procesgrænser og I/O-scheduler, så mange samtidige PHP-arbejdere ikke går i stå. Hvis du ønsker at planlægge mere detaljeret, finder du en fokuseret Hostingoptimering konkrete tiltag for at adskille GC-kørsler og Forsinkelse at stabilisere.

Sessions i filsystemet vs. RAM-lagre

Filbaserede sessioner er enkle, men forårsager meget Overhead ved søgning, kontrol og sletning. RAM-baserede lagre som Redis eller Memcached administrerer nøgler effektivt, leverer hurtigt og har indbyggede udløbsmekanismer. Det sparer systemkald, reducerer ventetider og mindsker fejlfrekvensen ved filspærringer. Jeg foretrækker RAM-lagring, så snart antallet af besøgende stiger, eller admin-området reagerer langsomt. Overgangen kan gennemføres hurtigt, og en vejledning til Sessionhåndtering med Redis hjælper med at gøre konfigurationen klar og Ressourcer bedre udnyttelse.

Smarte PHP-indstillinger til sessioner

Jeg indstiller garbage collection, så ingen forespørgsel tilfældigt udløser. Til dette sætter jeg sandsynligheden til nul, planlægger oprydningen via Cron og justerer levetiden i forhold til risikoen. Desuden aktiverer jeg strenge tilstande, så PHP kun accepterer gyldige ID'er. Jeg kontrollerer hukommelse og sti, så ingen langsomme NFS-mounts eller overfyldte mapper bremser. Følgende oversigt viser almindelige standardværdier og afprøvede værdier, som jeg vælger afhængigt af anvendelsestilfældet for at Ydelse forbedres målbart.

Indstilling Typisk standard Anbefaling Effekt
session.gc_maxlifetime 1440 sekunder 900–3600 sekunder Kortere levetid reducerer gamle filer og sænker I/O.
session.gc_probability / session.gc_divisor 1 / 1000 (hyppigt) 0 / 1 Ingen oprydning i anmodninger, Cron overtager Oprydning.
session.save_handler filer redis eller memcached RAM-lager reducerer filspærringer og forkorter Forsinkelser.
session.use_strict_mode 0 1 Kun gyldige ID'er, færre kollisioner og Risici.
session.save_path Systempad Egen hurtig sti Kort katalogdybde, lokal SSD, mindre Stat-Opfordringer.

Derudover bemærker jeg yderligere kontakter, der forbedrer stabiliteten og sikkerheden uden at skabe ekstra omkostninger:

  • session.use_only_cookies=1, session.use_cookies=1 for klar cookie-brug uden URL-id'er.
  • session.cookie_httponly=1, session.cookie_secure=1 (ved HTTPS) og en passende session.cookie_samesite (oftest Lax) for at undgå lækager.
  • session.lazy_write=1 for at spare unødvendige skriveadgange, når indholdet ikke ændres.
  • session.serialize_handler=php_serialize for moderne serialisering og interoperabilitet.
  • Hæv session.sid_length og session.sid_bits_per_character for at gøre ID'er mere robuste.

Konkret konfiguration: php.ini, .user.ini og FPM

Jeg forankrer indstillingerne der, hvor de virker pålideligt: globalt i php.ini, via pool i PHP‑FPM eller lokalt via .user.ini i projekter, der har separate behov. Et pragmatisk sæt ser sådan ud:

; php.ini eller FPM-pool session.gc_probability = 0 session.gc_divisor = 1 session.gc_maxlifetime = 1800 session.use_strict_mode = 1 session.use_only_cookies = 1 session.cookie_httponly = 1
session.cookie_secure = 1 session.cookie_samesite = Lax session.lazy_write = 1 ; hurtigere, lokal sti eller RAM-lager ; session.save_handler = files ; session.save_path = "2;/var/lib/php/sessions"

I FPM-poolen kan jeg fastsætte værdier, så enkelte apps ikke overskriver dem:

; /etc/php/*/fpm/pool.d/www.conf php_admin_value[session.gc_probability] = 0
php_admin_value[session.gc_divisor] = 1 php_admin_value[session.gc_maxlifetime] = 1800 php_admin_value[session.save_path] = "2;/var/lib/php/sessions"

Filsystem og layout af gemme-sti

Store mapper med hundredtusindvis af filer er langsomme. Derfor deler jeg session-mappen op i undermapper, så mappeopslagene forbliver korte:

session.save_path = "2;/var/lib/php/sessions"

Den førende 2 opretter to niveauer af undermapper baseret på hash-dele af session-id'et. Derudover hjælper mount-indstillinger som noatime samt et filsystem med gode katalogindekser. Jeg undgår NFS til sessioner, hvis det er muligt, eller tvinger sticky sessions på load balancer, indtil en RAM-lager er produktiv.

Deaktiver låsning i koden

Mange forsinkelser skyldes ikke kun GC, men også unødvendigt lange låse. Jeg åbner sessionen så kort som muligt:

<?php session_start(); // læs $data = $_SESSION['key'] ?? null; session_write_close(); // Lås tidligt // dyrt arbejde uden lås $result = heavy_operation($data);

// Åbn og skriv kun igen, hvis det er nødvendigt session_start(); $_SESSION['result'] = $result; session_write_close();

Hvis jeg kun læser, starter jeg sessionen med read_and_close, så PHP slet ikke går i skrivemodus:

true]); // kun læsning, ingen skrivning nødvendig

Dette mindsker sandsynligheden for, at parallelle anmodninger skal vente på hinanden. I WordPress-plugins kontrollerer jeg, om session_start() overhovedet er nødvendigt, og flytter opkaldet til sene hooks, så kerneflowet ikke blokeres.

RAM-lagerkonfiguration til sessioner

Hos Redis eller Memcached skal jeg være opmærksom på timeouts, databaser og lagerpolitik. Et robust eksempel på Redis ser således ud:

session.save_handler = redis session.save_path = "tcp://127.0.0.1:6379?database=2&timeout=2&read_timeout=2&persistent=1" session.gc_maxlifetime = 1800 session.gc_probability = 0 session.gc_divisor = 1

Da RAM-lagre selv styrer udløbstider, sparer jeg mig for fil-GC. Jeg kører sessioner adskilt fra caches (anden DB eller nøglepræfiks), så evictions for cache-nøgler ikke uønsket kasserer sessioner. Jeg indstiller lagerpolitikken til volatile-LRU, så kun nøgler med TTL fortrænges, når lagerpladsen bliver knap.

Ekstern oprydning via Cron: Sådan udligner jeg anmodninger

Jeg opnår den sikreste afkobling ved at placere GC uden for request-flowet. start. Jeg sætter sandsynligheden i PHP-ini eller via .user.ini til 0 og kalder regelmæssigt et lille script via Cron, der starter oprydningen. Cron kører ideelt set hvert minut eller hvert femte minut, afhængigt af trafikken og den ønskede hygiejne. Det er vigtigt, at Cron kører med den samme bruger som webserveren, så tilladelserne stemmer. Derudover kontrollerer jeg logfiler og målinger for at sikre, at den planlagte Rutine fungerer pålideligt.

Til filbaserede sessioner bruger jeg to gennemprøvede varianter:

  • En PHP-enkeltlinje, der kalder den interne GC (fra PHP 7.1):
*/5 * * * * php -d session.gc_probability=1 -d session.gc_divisor=1 -r 'session_gc();' 2>/dev/null
  • En find-oprydning, der sammenligner mtime med den ønskede levetid:
*/5 * * * find /var/lib/php/sessions -type f -mmin +30 -delete

Jeg holder øje med Cron-køretiden. Hvis fem minutter ikke er nok, øger jeg frekvensen eller sænker levetiden. I meget trafikerede opsætninger kører Cron hvert minut for at holde mappen lille.

Diagnose og overvågning

Jeg genkender GC-toppe ved forhøjede responstider og markante I/O-toppe i Overvågning. Værktøjer i WordPress-sammenhæng, såsom Query Monitor, hjælper med at identificere langsomme hooks, plugins og admin-kald. Et kig i access- og error-logs viser, hvornår anmodninger tager betydeligt længere tid. Mange små 200 ms-spidser er normale, men afvigelser på flere sekunder indikerer låsning eller GC. Hvis man desuden observerer antallet af filer og størrelsen på mappen, kan man se, hvordan sessionsmappen fyldes, og hvorfor en planlagt Oprydning er nødvendigt.

Praktiske hjælpemidler til årsagsanalyse:

  • Aktivér php-fpm slowlog og request_slowlog_timeout for at se blokerende steder.
  • iotop, iostat, pidstat og vmstat til at registrere I/O-tryk og kontekstskift.
  • strace -p kortvarigt for at overvåge åbne filer og låse.
  • find | wc -l på session-stien for at måle filmængden.
  • TTFB- og p95/p99-latenser i APM for at kvantificere forbedringer efter overgangen.

WordPress-specifikke kontroller

Jeg tjekker plugins, der kalder session_start() tidligt, og erstatter kandidater med unødvendig session-brug med Alternativer. I Admin reducerer jeg heartbeat-frekvensen eller begrænser den til redigeringssiderne. Caches må ikke omgå sessioner, ellers forsvinder effekten; derfor kontrollerer jeg undtagelser omhyggeligt. Også vigtigt: ingen session for gæster, hvis der ikke er nogen grund til det. Således reduceres mængden af filer pr. dag mærkbart, og den planlagte GC har mindre at lave.

I WooCommerce-miljøer er jeg særlig opmærksom på indkøbskurv- og fragmentfunktioner, der opretter sessioner for anonyme brugere. Ofte er det tilstrækkeligt kun at starte sessioner ved reelle interaktioner (login, checkout). Derudover sikrer jeg mig, at WP-Cron ikke forårsager for stor belastning parallelt: Jeg lader WP-Cron starte fra en system-Cron og deaktiverer udførelsen pr. anmodning. Dette forhindrer, at Cron-jobs kolliderer med session-operationer.

Sikkerhed, levetid og brugeroplevelse

Længere levetider holder brugerne logget ind, men øger mængden af gamle Sessioner. Kortere værdier reducerer belastningen, men kan afslutte logins tidligere. Jeg vælger derfor perioder, der afbalancerer risiko og komfort, for eksempel 30-60 minutter i admin og kortere for anonyme brugere. Ved særligt følsomt indhold indstiller jeg strenge tilstande og sikre cookies mod XSS og transportfejl. På den måde forbliver data beskyttet, mens Ydelse forbliver pålidelig.

Efter login roterer jeg session-id'er (session_regenerate_id(true)) for at undgå fiksering og bruger konsekvent cookie_same_site, httponly og secure. I single-sign-on-scenarier planlægger jeg bevidst SameSite-valget (Lax vs. None), så brugeroplevelsen forbliver stabil.

Klynger, load balancers og sticky sessions

Hvis du driver flere app-servere, bør du kun bruge filbaserede sessioner med sticky sessions, ellers mister brugerne deres status. Det er bedre at bruge en central RAM-lagerplads. Jeg tjekker latenstiden mellem appen og lagerpladsen, indstiller timeouts til at være korte, men ikke aggressive, og planlægger en failover (f.eks. Sentinel/Cluster hos Redis). Ved vedligeholdelse er det vigtigt at vælge TTL'er, så en kort nedbrud ikke straks fører til masseudlogninger.

Økonomisk afvejning og migrationsvej

Et skift til Redis eller Memcached koster drift, men sparer penge Tid pr. anmodning og reducerer supporttilfælde. Hvis du ofte arbejder i Admin, mærker du straks forskellen. Jeg vurderer besparelserne ved hurtigere implementeringer, mindre frustration og færre afbrydelser. Når belastningen stiger, planlægger jeg migrationen tidligt i stedet for sent for at undgå flaskehalse. En klar køreplan omfatter test, udrulning og overvågning, indtil Forsinkelse stabil, og GC-kørslerne forbliver upåfaldende.

Jeg går trinvist frem med overgangen: I staging aktiverer jeg RAM-lageret, kører syntetisk belastning og kontrollerer p95/p99-latenser. Derefter ruller jeg ud i små procenter via feature-flag og observerer fejlprocenter og timeouts. En rollback forbliver enkel, hvis jeg kan variere session.name parallelt, så sessioner mellem det gamle og det nye backend ikke kolliderer. Vigtige nøgletal er: Sessionfiler pr. time (skal falde), median TTFB (skal falde), 5xx-rate (skal forblive stabil) og andel af anmodninger med sessionslås over 100 ms (skal falde kraftigt).

Kort opsummeret

Php session gc forårsager forsinkelser, fordi tilfældigt startede oprydningskørsler medfører lange filoperationer og låsninger. udløse. Jeg afhjælper dette ved at sætte sandsynligheden i anmodninger til nul, planlægge oprydningen via Cron og placere sessioner i RAM-lagre. Hostingressourcer med hurtig NVMe og tilstrækkelig RAM reducerer blokeringen yderligere. WordPress drager mærkbar fordel af, at Heartbeat begrænses, plugins gennemgås og unødvendige sessioner undgås. Hvis man følger disse trin, reducerer man svartiderne, forhindrer blokeringer og holder Administrator-Overfladen er reaktionsdygtig, også ved høj trafik.

Aktuelle artikler