Una pàgina que respon en 200 ms contra un Redis local triga quatre segons quan el client se situa darrere d’una VPN, mentre els comptadors del servidor queden inactius. El registre lent no mostra res per sobre d’un mil·lisegon, el gràfic de CPU és pla i la instància serveix altres clients sense queixes. El que ha canviat és la distància, i el que multiplica la distància és el nombre de vegades que el vostre codi s’atura i espera. Recorreu el camí de la petició des del client cap enfora i la solució gairebé sempre és menys esperes seqüencials que no pas un servidor més ràpid.

Començeu pel client, no per les mètriques del servidor

Observeu el procés del client entre la primera ordre i l’última. Si la traça mostra una resposta, una pausa i després l’ordre següent, pagueu un viatge complet per ordre i cap ajust del servidor ho canviarà. El registre lent és l’instrument equivocat aquí, perquè enregistra el temps d’execució i exclou deliberadament l’E/S de comunicació. Un registre lent net al costat d’un punt final lent és la signatura d’un problema de viatges d’anada i tornada.

Mesureu el camí mateix abans de tocar el codi. redis-cli -h redis.example.internal --latency envia PING repetits i imprimeix el temps mitjà de petició i resposta, que és el vostre cost per intercanvi. Multipliqueu-lo pel nombre d’ordres seqüencials en una acció d’usuari i tindreu un mínim per al punt final.

Compteu els viatges que una acció d’usuari realment necessita

Agafeu un sol punt final i anoteu cada crida a Redis per ordre, incloses les que s’amaguen dins d’un ORM, un middleware de sessió o una cerca de feature flag. La majoria de punts finals lents fan vint o trenta crides on tres bastarien, i les amagades solen ser la majoria.

Una cadena de dependències no es pot col·lapsar: si la segona ordre necessita la clau que retorna la primera, l’espera és real i les úniques opcions són un model de dades diferent o una operació al servidor que expressi tot el pas alhora. Les lectures independents són un altre cas, perquè res no us obliga a esperar la primera resposta abans d’enviar la segona. Marqueu cada crida com a dependent o independent abans d’editar res i la forma de la solució es fa evident.

El nombre que importa són les esperes seqüencials, no les ordres totals. Trenta ordres en un lot encara costen una espera.

Agrupeu només les ordres que no depenen les unes de les altres

Una canonada posa en cua ordres independents i recull les respostes en ordre, convertint N esperes en una. La majoria de clients l’exposen com a bloc, callback o gestor de context en lloc d’un mode de connexió separat, així que comproveu l’API que ofereix la versió instal·lada. El guany creix amb el temps de viatge, i per això mateix importa en una VPN i gairebé no es nota a 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())

Passeu transaction=False quan només voleu agrupar. Amb el valor per defecte el client embolcalla el lot en MULTI i EXEC, cosa que el fa atòmic i canvia què poden observar altres clients mentre s’executa. L’atomicitat és una decisió de correcció, no un botó de rendiment, i una canonada no és una transacció només perquè totes dues redueixin viatges.

Llegiu cada resposta en lloc de només l’enviament del lot. Una ordre en cua pot retornar un error mentre el lot en conjunt es completa, i un client que llença la llista de respostes converteix una escriptura fallida en una de silenciosa. Compareu la longitud de la llista de respostes amb el nombre d’ordres que heu posat en cua.

Manteniu la connexió viva entre accions

Cada connexió nova paga la resolució de noms, un handshake TCP, un handshake TLS i un AUTH, i per VPN cadascun d’aquests és un viatge que no podeu eliminar agrupant. Creeu el client una vegada per procés i reutilitzeu-lo, o useu el pool que ofereix la vostra biblioteca amb una mida limitada. Reconectar per petició és la segona causa més comuna d’aquest símptoma després de les esperes per ordre.

Manteniu el nom d’amfitrió privat a la configuració en lloc d’una adreça literal. La validació del certificat i la identitat del servei encara s’apliquen en una xarxa privada, i fixar una IP es trenca en el moment que el proveïdor renumera una subxarxa. Si la resolució és la part lenta, arregleu el camí del resolutor en lloc de la cadena de connexió.

Definiu un temps d’espera de socket explícit perquè un túnel encallat falli ràpid en lloc de penjar un treballador. Després confirmeu que el pool es reutilitza realment registrant la creació de connexions al client.

Dimensioneu el lot abans que es converteixi en un problema de memòria

Redis reté les respostes en cua durant tot el lot, i el client reté després el resultat analitzat. Una canonada de deu mil valors grans canvia esperes de xarxa per pics de memòria i pauses llargues als dos extrems.

Trieu la mida del lot segons el volum de resposta i no el nombre d’ordres. Uns quants centenars d’ordres petites en un lot solen ser còmodes, mentre que un centenar de càrregues de diversos kilobytes no ho són. Dividiu per bytes i també per nombre, i manteniu cada lot dins del pressupost de latència que el punt final té realment.

Mesureu d’extrem a extrem després del canvi: redis-cli -h redis.example.internal --latency-history mostra si el camí mateix és estable, i el vostre propi temporitzador de peticions mostra si el punt final ha millorat. Si el punt final continua lent mentre l’historial de latència és pla, el cost restant és feina al servidor o la vostra pròpia serialització, i el següent pas és un perfil del gestor en lloc d’un altre canvi d’agrupació.

Articles relacionats