Redis prin VPN pare lent: mai puține dus-întorsuri pe cerere
Un VPN nu face dintr-un apel Redis la distanță unul local. Bugetați dus-întorsurile pe cerere, refolosiți conexiunile și țineți conductele departe de tranzacții.
O pagină care răspunde în 200 ms către un Redis local durează patru secunde de îndată ce clientul stă în spatele unui VPN, în timp ce contoarele serverului rămân inactive. Jurnalul lent nu arată nimic peste o milisecundă, graficul CPU este plat, iar instanța deservește alți clienți fără reclamații. Ce s-a schimbat este distanța, iar ceea ce multiplică distanța este numărul de ori în care codul dvs. se oprește și așteaptă. Parcurgeți calea cererii de la client spre exterior și soluția este aproape întotdeauna mai puține așteptări secvențiale, nu un server mai rapid.
Începeți de la client, nu de la metricile serverului
Observați procesul clientului între prima și ultima comandă. Dacă urma arată un răspuns, o pauză și apoi comanda următoare, plătiți un dus-întors complet pentru fiecare comandă și nicio reglare a serverului nu va schimba asta. Jurnalul lent este instrumentul greșit aici, pentru că înregistrează timpul de execuție și exclude deliberat I/O de comunicație. Un jurnal lent curat lângă un endpoint lent este semnătura unei probleme de dus-întors.
Măsurați calea în sine înainte de a atinge codul. redis-cli -h redis.example.internal --latency trimite PING-uri repetate și tipărește timpul mediu de cerere și răspuns, care este costul dvs. pe schimb. Înmulțiți-l cu numărul de comenzi secvențiale dintr-o acțiune a utilizatorului și aveți o limită inferioară pentru endpoint.
Numărați dus-întorsurile de care are nevoie cu adevărat o acțiune a utilizatorului
Luați un singur endpoint și notați fiecare apel Redis în ordine, inclusiv cele ascunse într-un ORM, într-un middleware de sesiune sau într-o căutare de feature flag. Majoritatea endpointurilor lente ajung să facă douăzeci sau treizeci de apeluri acolo unde trei ar fi de ajuns, iar cele ascunse sunt de obicei majoritatea.
Un lanț de dependențe nu poate fi pliat: dacă a doua comandă are nevoie de cheia pe care o returnează prima, așteptarea este reală și singurele opțiuni sunt un alt model de date sau o operație pe server care exprimă tot pasul deodată. Citirile independente sunt alt caz, pentru că nimic nu vă obligă să așteptați primul răspuns înainte de a-l trimite pe al doilea. Marcați fiecare apel ca dependent sau independent înainte de a modifica ceva, iar forma soluției devine evidentă.
Numărul care contează sunt așteptările secvențiale, nu comenzile totale. Treizeci de comenzi într-un lot costă tot o singură așteptare.
Grupați doar comenzile care nu depind unele de altele
O conductă pune în coadă comenzi independente și colectează răspunsurile în ordine, transformând N așteptări într-una singură. Majoritatea clienților o expun ca bloc, callback sau manager de context în loc de un mod de conexiune separat, așa că verificați API-ul oferit de versiunea instalată. Câștigul crește cu timpul dus-întors, și exact de aceea contează pe un VPN și abia se observă pe localhost.
import redis
r = redis.Redis(host="redis.example.internal", port=6379, socket_timeout=2)
pipe = r.pipeline(transaction=False)
for key in ("user:1001", "user:1002", "user:1003"):
pipe.get(key)
print(pipe.execute())
Treceți transaction=False când vreți doar gruparea. În mod implicit clientul înfășoară lotul în MULTI și EXEC, ceea ce îl face atomic și schimbă ce pot observa alți clienți în timpul zborului. Atomicitatea este o decizie de corectitudine, nu un buton de performanță, iar o conductă nu este o tranzacție doar pentru că ambele reduc dus-întorsurile.
Citiți fiecare răspuns, nu doar trimiterea lotului. O comandă din coadă poate returna o eroare în timp ce lotul ca întreg se finalizează, iar un client care aruncă lista de răspunsuri transformă o scriere eșuată într-una silențioasă. Comparați lungimea listei de răspunsuri cu numărul de comenzi puse în coadă.
Țineți conexiunea vie între acțiuni
Fiecare conexiune nouă plătește rezolvarea numelor, un handshake TCP, un handshake TLS și un AUTH, iar prin VPN fiecare dintre acestea este un dus-întors pe care nu îl puteți grupa. Creați clientul o dată pe proces și refolosiți-l, sau folosiți pool-ul oferit de bibliotecă cu o dimensiune limitată. Reconectarea pentru fiecare cerere este a doua cea mai frecventă cauză a acestui simptom după așteptările pe comandă.
Țineți numele de gazdă privat în configurație în locul unei adrese literale. Validarea certificatului și identitatea serviciului se aplică în continuare într-o rețea privată, iar fixarea unui IP se rupe în momentul în care furnizorul renumerotează o subrețea. Dacă rezolvarea este partea lentă, reparați calea resolver-ului, nu șirul de conexiune.
Setați un timeout de socket explicit, astfel încât un tunel blocat să eșueze rapid în loc să atârne un worker. Apoi confirmați că pool-ul este într-adevăr refolosit, înregistrând crearea conexiunilor în client.
Dimensionați lotul înainte de a deveni o problemă de memorie
Redis ține răspunsurile din coadă pe toată durata unui lot, iar clientul ține apoi rezultatul analizat. O conductă de zece mii de valori mari schimbă așteptările de rețea pe vârfuri de memorie și pauze lungi la ambele capete.
Alegeți dimensiunea lotului după volumul răspunsului, nu după numărul de comenzi. Câteva sute de comenzi mici într-un lot sunt de obicei confortabile, în timp ce o sută de încărcături de câțiva kilobyți nu sunt. Împărțiți după bytes și după număr, și țineți fiecare lot în bugetul de latență pe care endpointul îl are cu adevărat.
Măsurați de la un capăt la altul după modificare: redis-cli -h redis.example.internal --latency-history arată dacă calea în sine este stabilă, iar propriul dvs. cronometru de cereri arată dacă endpointul s-a îmbunătățit. Dacă endpointul este încă lent în timp ce istoricul de latență este plat, costul rămas este muncă pe server sau propria serializare, iar pasul următor este un profil al handler-ului, nu o altă modificare de grupare.