Collo di bottiglia nel server o nell'applicazione: come misurarlo
Il time to first byte a ogni hop, i contatori di margine sulla macchina e uno slow log ordinato per frame bloccante collocano un sito lento in un solo livello prima che qualcosa venga cambiato.
In questo articolo 7 sezioni
- Misurate il time to first byte a ogni hop: attraverso l'edge, direttamente sull'origin, e su localhost. Il divario più grande tra due numeri adiacenti è il livello a cui appartiene la latenza.
- Leggete il margine della macchina mentre è lenta: CPU idle e steal, memoria disponibile, await e profondità di coda dei dischi. Margine ovunque sotto un sito lento significa che il vincolo sta sopra l'hardware.
- Dentro il livello applicativo, aggregate: richieste per URL, richieste lente per frame bloccante, query per digest. Ognuno comprime migliaia di righe in una breve lista ordinata con uno o due nomi in cima.
Il time to first byte a ogni hop
Le teorie su un sito lento sono facili da formulare e difficili da chiudere. Tre misure le chiudono. Prendete il time to first byte attraverso l'edge pubblico, di nuovo direttamente contro l'origin con l'edge aggirato, e di nuovo sull'host dell'applicazione contro localhost. La differenza tra due letture adiacenti è il costo di quel livello.
# through the edge
curl -s -o /dev/null -w 'dns=%{time_namelookup} tls=%{time_appconnect} ttfb=%{time_starttransfer}\n' https://site.example/
# direct to the origin, edge bypassed
curl -s -o /dev/null -w 'ttfb=%{time_starttransfer}\n' --resolve site.example:443:ORIGIN_IP https://site.example/
# on the host, nothing in front of the application
curl -s -o /dev/null -w 'ttfb=%{time_starttransfer}\n' http://127.0.0.1/Ciò che resta su localhost è l'applicazione e i servizi che chiama. Il livello con il divario più grande è dove va il resto del lavoro.
Il margine mentre fa male
Leggete i contatori durante il periodo lento, non dopo.
vmstat 1 5 # r (run queue), wa (I/O wait), st (steal), id (idle)
iostat -x 1 5 # await and aqu-sz per device
free -m # the available column, not usedUna run queue lunga con poco idle è una macchina limitata dalla CPU. Un I/O wait alto con await alto sul dispositivo è lo storage. Lo steal è un vicino su un hypervisor condiviso che si prende cicli che il guest voleva. CPU idle, memoria disponibile e dispositivi con await basso sotto un sito lento significano che la macchina sta aspettando qualcosa che non le appartiene: una chiamata esterna, un lock, una query lenta per ragioni diverse dall'hardware. Aggiungere hardware a quel punto produce una macchina più scarica che è ancora lenta.
C'è una terza categoria tra le due, ed è comune: il pool. La pagina di stato di PHP-FPM mostra una listen queue; una coda di richieste in attesa con CPU idle significa che ogni worker è occupato, di solito bloccato sulla stessa chiamata lenta, e il sito è lento per un tetto di configurazione, non per un tetto di risorse. La stessa forma compare come un pool di connessioni al database saturo o una coda di backend HAProxy piena. La macchina è innocente e il codice non è ancora stato condannato; la dimensione del pool e la chiamata su cui sta aspettando sono le prossime due letture.
Leggete il traffico prima del codice
L'access log dice che tipo di problema è questo. Trovate prima il log per vhost; il log predefinito può contenere una piccola frazione del traffico reale, e un conteggio sul file sbagliato dà una risposta sbagliata con grande sicurezza.
tail -n 100000 access.log | awk -F'"' '{print $2}' | sort | uniq -c | sort -rn | headPoche URL ripetute migliaia di volte è il sito che chiama sé stesso, e la soluzione è una cache davanti a quelle URL. Richieste che marciano in ordine di sitemap sono un crawler, e la soluzione è un rate limit. Un aumento ampio e uniforme su molte URL è domanda, e la soluzione è capacità.
Comprimete lo slow log e il log delle query
Uno slow log è un mucchio di stack trace finché non viene indicizzato per chiave. Attivate lo slow log di PHP-FPM con una soglia che catturi le richieste di cui gli utenti si lamentano, poi indicizzate ogni traccia sul frame in cui era bloccata, la foglia, e contate. La lista ordinata è breve e in cima c'è di solito una chiamata esterna o una query.
Sul lato database, ordinate per digest, non per piano. Una parametrizzazione incoerente spezza una singola query logica in molti piani che sembrano ciascuno innocuo; sommati per digest, il vero proprietario del tempo del database si ricompone in una riga.
-- MySQL and Percona: by statement digest
SELECT digest_text, count_star, sum_timer_wait
FROM performance_schema.events_statements_summary_by_digest
ORDER BY sum_timer_wait DESC
LIMIT 10;
-- SQL Server: by query hash
SELECT query_hash, COUNT(*) AS plans, SUM(total_worker_time) AS cpu
FROM sys.dm_exec_query_stats
GROUP BY query_hash
ORDER BY cpu DESC;Prima di scrivere qualsiasi indice, verificate il numero reale di righe e gli indici già esistenti. Un indice su una tabella che non ne ha bisogno è un percorso di scrittura più lento.
Leggete la fonte primaria, non una storia plausibile
| La storia | La fonte che la chiude |
|---|---|
| È andato in crash, ha finito la memoria | dmesg, journalctl --list-boots, last -x reboot: ha fatto reboot, e perché |
| Il database è corrotto | l'error log del database stesso, cercando i messaggi di crash e di corruzione |
| Un processo impazzito sta mangiando la CPU | ps --forest e /proc/<pid>/cmdline: la sua vera identità e il suo padre, non un nome plausibile |
| Vediamo errori di integrità | ethtool -S sull'interfaccia: zero errori sul cavo significa che il problema sta sopra il cavo |
| SELinux lo sta bloccando | ausearch -m avc -ts recent passato a audit2why: il diniego, nominato, con il modulo di policy che lo risolve |
Ogni riga sostituisce una teoria con una lettura. L'ultima conta su un sistema in enforcing: l'audit log risponde alla domanda senza cambiare la postura di sicurezza per porla.
Dimostratelo, poi verificate con la stessa misura
La correlazione dà un sospetto. Una piccola perturbazione reversibile lo condanna: fermate le query sospette per qualche secondo e guardate se il carico si riempie di nuovo. Se si riempie subito, il traffico live è il motore e la correzione va all'edge, davanti alle richieste che non dovrebbero raggiungere affatto il database. Finché la causa non è nota, il sistema è in sola lettura. Quando la correzione è in produzione, si verifica con la misura che ha trovato il problema: se il time to first byte per hop lo ha localizzato, il time to first byte per hop conferma che è sparito.
Cosa sistemiamo noi e cosa vi restituiamo
| All'edge, lo stesso giorno | Nel vostro codice, con la modifica esatta |
|---|---|
| Micro-caching con collapsing delle richieste davanti alle URL calde | Spostare la chiamata sincrona a terze parti in un job in background con un timeout e un fallback |
| Pool di worker e di buffer dimensionati sul carico misurato | Rimuovere il ciclo di rendering o il ciclo di retry |
| Un indice covering aggiunto online | Far scadere i dati applicativi che crescono senza limite |
| Retry e timeout di HAProxy ottimizzati, un contesto SELinux corretto | Ridurre il payload e gli script spediti al browser |
Non modifichiamo il vostro codice. Nominiamo la riga, mostriamo la misura che la indica e teniamo su il sito mentre la sistemate.
Risposte dirette
Come faccio a sapere se è il mio server o la mia applicazione a essere lenta?+
Leggete il margine durante il periodo lento: CPU idle e steal, memoria disponibile, await dei dischi. Se tutti e tre hanno spazio, il vincolo sta sopra l'hardware, nell'applicazione o in un servizio che chiama. Il time to first byte su localhost confrontato con l'edge conferma quale livello.
Come trovate una query lenta nel database?+
Ordinando per digest, non per plan handle, così che una singola query logica con molte parametrizzazioni mostri il suo costo totale in una riga. Poi verifichiamo i conteggi delle righe e gli indici esistenti prima di scrivere qualsiasi DDL.
Sapete distinguere un bot da un picco di traffico reale?+
Dall'access log, per frequenza delle URL. Una manciata di URL ripetute migliaia di volte è il sito che chiama sé stesso, l'ordine di sitemap è un crawler, un aumento ampio è domanda. Ciascuno ha una correzione diversa.
Modificate il mio codice?+
No. Tutto ciò che sta sul lato infrastruttura lo sistemiamo all'edge lo stesso giorno, in modo reversibile. Tutto ciò che sta nel vostro codice ve lo restituiamo come modifica esatta, con la misura che la giustifica.
Il vostro sito è lento e nessuno è d'accordo sul perché.
Parlate con un ingegnere che misura ogni hop prima di toccare qualsiasi cosa.
Parla con un ingegnereSiete arrivati alla fine.
Mettete la lettura al lavoro
Cosa fate girare?
Raccontateci il carico di lavoro, il traffico e i requisiti di ripristino. Partite dal configuratore di server, oppure discutete i dettagli con un ingegnere.
Scritto dagli ingegneri che costruiscono e gestiscono l'infrastruttura che descrive.Parla con un ingegnere
