Come ridimensionare php5 + MySQL oltre 200 richieste / secondo?


16

Sto modificando la mia homepage per le prestazioni, attualmente gestisce circa 200 richieste / secondo su 3.14.by che mangia 6 query SQL e 20 req / secondo su 3.14.by/forum che è il forum phpBB.

Stranamente, i numeri sono più o meno gli stessi su alcuni server VPS e Atom 330 dedicati.

Il software server è il seguente: Apache2 + mod_php prefork 4 child (qui provato diversi numeri), php5, APC, nginx, memcached per l'archiviazione delle sessioni PHP.

MySQL è configurato per consumare circa il 30% della RAM disponibile (~ 150 Mb su VPS, 700 Mb su server dedicato)

Sembra che ci sia un collo di bottiglia da qualche parte che non mi permette di andare più in alto, qualche suggerimento? (vale a dire che so che fare meno di 6 SQL lo renderebbe più veloce, ma questo non sembra un fattore limitante, poiché sqld mangia non più di qualche% in cima a causa delle query memorizzate nella cache)

Qualcuno ha testato che calciare precaricato apache2 e lasciare solo nginx + php è molto più veloce?

Alcuni altri parametri di riferimento

Small 40-byte static file: 1484 r/s via nginx+apache2, 2452 if we talk to apache2 directly. 
Small "Hello world" php script: 458 r/s via ngin+apache2.

Aggiornamento: sembra che il collo di bottiglia sia rappresentato dalle prestazioni di MySQL sui dati memorizzati nella cache. La pagina con SQL singolo mostra 354req / sec, con 6 SQL - 180 req / sec. Cosa pensi che posso modificare qui? (Posso sborsare 100-200 Mb per MySQL)

[client]
port        = 3306
socket      = /var/run/mysqld/mysqld.sock

[mysqld_safe]
socket      = /var/run/mysqld/mysqld.sock
nice        = 0

[mysqld]
default-character-set=cp1251
collation-server=cp1251_general_cs

skip-character-set-client-handshake

user        = mysql
pid-file    = /var/run/mysqld/mysqld.pid
socket      = /var/run/mysqld/mysqld.sock
port        = 3306
basedir     = /usr
datadir     = /var/lib/mysql
tmpdir      = /tmp
skip-external-locking

bind-address        = 127.0.0.1

key_buffer      = 16M
max_allowed_packet  = 8M
thread_stack        = 64K
thread_cache_size   = 16
sort_buffer_size    = 8M
read_buffer_size    = 1M

myisam-recover      = BACKUP
max_connections        = 650
table_cache            = 256
thread_concurrency     = 10

query_cache_limit       = 1M
query_cache_size        = 16M

expire_logs_days    = 10
max_binlog_size         = 100M

[mysqldump]
quick
quote-names
max_allowed_packet  = 8M

[mysql]
[isamchk]
key_buffer      = 8M

!includedir /etc/mysql/conf.d/

Perché stai usando sia Apache che Nginx?
Jamieb,

Questa è la configurazione comune, da Apache2 a PHP e varie app che richiedono l'infrastruttura di Apache, nginx per ridurre il footprint di memoria di Apache2 al caricamento.
BarsMonster,

In realtà, non capisco il tuo problema. Il tuo sito è attualmente lento? In tal caso, quanto è lento? E quanto desideri accelerare? Hai provato a profilare alcune parti del tuo sito per determinare dove si trova il collo di bottiglia?
Jamieb,

È nella descrizione: ora è su 180-200 richieste / secondo. Anche se questo è molto più che sufficiente per una homepage, voglio modificare questa configurazione per fare in modo che altri siti basati sullo stesso codice funzionino più velocemente. Idealmente, desidero saturare la connessione 100Mbit con pagine dinamiche :-)
BarsMonster,

2
"Richieste al secondo" non è in realtà una metrica significativa in questo contesto. Il mio netbook è in grado di gestire "200 richieste al secondo". Devi dirci quale tempo di risposta vuoi raggiungere con quel tipo di velocità di connessione.
Jamieb,

Risposte:


29

Ovviamente, ci sono molte cose che puoi provare. La tua scommessa migliore è inseguire i tuoi log per query che non usano indici (abilita i log per quelli) e altre query non ottimizzate. Nel corso degli anni ho compilato un vasto elenco di opzioni relative alle prestazioni, quindi ho incluso un piccolo sottoinsieme qui per vostra informazione - si spera che aiuti. Ecco alcune note generali per le cose che puoi provare (se non l'hai già fatto):

MySQL

  • query_cache_type = 1 - le query SQL della cache sono attive. Se impostato su 2, le query vengono memorizzate nella cache solo se viene passato il suggerimento SQL_CACHE. Analogamente al tipo 1, è possibile disabilitare la cache per una determinata query con il suggerimento SQL_NO_CACHE
  • key_buffer_size = 128M (predefinito: 8M) - buffer di memoria per gli indici delle tabelle MyISAM. Su server dedicati, punta a impostare key_buffer_size su almeno un quarto, ma non più della metà, della quantità totale di memoria sul server
  • query_cache_size = 64M (impostazione predefinita: 0) - dimensione della cache della query
  • back_log = 100 (impostazione predefinita: 50, max: 65535) - La coda delle richieste di connessione in sospeso. Importa solo quando ci sono molte connessioni in breve tempo
  • join_buffer_size = 1M (impostazione predefinita: 131072) - un buffer utilizzato quando si hanno scansioni di tabelle complete (senza indici)
  • table_cache = 2048 (impostazione predefinita: 256): devono essere max_user_connections moltiplicati per il numero massimo di JOIN contenuti nella query SQL più pesante. Utilizzare la variabile "open_tables" nelle ore di punta come guida. Guarda anche la variabile "open_tables" - dovrebbe essere vicino a "open_tables"
  • query_prealloc_size = 32K (impostazione predefinita: 8K) - memoria persistente per l'analisi e l'esecuzione delle istruzioni. Aumenta se hai domande complesse
  • sort_buffer_size = 16M (impostazione predefinita: 2M) - aiuta con l'ordinamento (operazioni ORDER BY e GROUP BY)
  • read_buffer_size = 2M (predefinito: 128K) - Aiuta con scansioni sequenziali. Aumentare se ci sono molte scansioni sequenziali.
  • read_rnd_buffer_size = 4M - aiuta la tabella MyISAM ad accelerare la lettura dopo l'ordinamento
  • max_length_for_sort_data: dimensione della riga da archiviare anziché puntatore di riga nel file di ordinamento. Può evitare letture casuali della tabella
  • key_cache_age_threshold = 3000 (impostazione predefinita: 300) - tempo per mantenere la cache delle chiavi nella hot zone (prima che venga retrocessa in warm)
  • key_cache_division_limit = 50 (impostazione predefinita: 100) - abilita un meccanismo di sgombero della cache più sofisticato (due livelli). Indica la percentuale da mantenere per il livello inferiore. delay_key_write = ALL: il buffer delle chiavi non viene scaricato per la tabella ad ogni aggiornamento dell'indice, ma solo quando la tabella viene chiusa. Questo accelera molto le scritture sui tasti, ma se si utilizza questa funzione, è necessario aggiungere il controllo automatico di tutte le tabelle MyISAM avviando il server con l'opzione --myisam-recover = BACKUP, FORCE
  • memlock = 1 - processo di blocco in memoria (per ridurre lo scambio in / out)

Apache

  • cambia il metodo di spawn (ad esempio in mpm)
  • disabilitare i registri, se possibile
  • AllowOverride None - quando possibile disabilita .htaccess. Arresta apache per la ricerca di file .htaccess se non vengono utilizzati, quindi salva una richiesta di ricerca di file
  • SendBufferSize: impostato sul sistema operativo predefinito. Su reti congestionate, è necessario impostare questo parametro vicino alla dimensione del file più grande normalmente scaricato
  • KeepAlive Off (impostazione predefinita attivata) - e installa lingerd per chiudere correttamente le connessioni di rete ed è più veloce
  • DirectoryIndex index.php: mantiene l'elenco dei file il più breve e assoluto possibile.
  • Opzioni FollowSymLinks - per semplificare il processo di accesso ai file in Apache
  • Evita di usare mod_rewrite o almeno regex complessi
  • ServerToken = prod

PHP

  • variabili_ordine = "GPCS" (Se non hai bisogno di variabili d'ambiente)
  • register_globals = Off - oltre ad essere un rischio per la sicurezza, ha anche un impatto sulle prestazioni
  • Mantieni include_path il più minimamente possibile (evita ricerche extra sul filesystem)
  • display_errors = Off - Disabilita la visualizzazione degli errori. Consigliato vivamente per tutti i server di produzione (non visualizza brutti messaggi di errore in caso di problemi).
  • magic_quotes_gpc = Off
  • magic_quotes _ * = Off
  • output_buffering = On
  • Disabilitare la registrazione, se possibile
  • expose_php = Off
  • register_argc_argv = Off
  • always_populate_raw_post_data = Off
  • posiziona il file php.ini dove php lo cercherebbe per primo.
  • session.gc_divisor = 1000 o 10000
  • session.save_path = "N; / path" - Per i siti di grandi dimensioni prendere in considerazione l'utilizzo. Suddivide i file di sessione in sottodirectory

Modifiche al sistema operativo

  • Montare i dischi rigidi usati con l'opzione -o noatime (nessun tempo di accesso). Aggiungi anche questa opzione al file / etc / fstab.
  • Modifica / proc / sys / vm / swappiness (da 0 a 100) per vedere quali sono i risultati migliori
  • Usa dischi RAM - mount --bind -ttmpfs / tmp / tmp

Questa è una bella lista, ne avevo già la maggior parte, e quando ho aggiunto cose rimanenti, le prestazioni non sono aumentate. Sembra che il collo di bottiglia sia da qualche parte tra PHP e MySQL che non è in grado di gestire più di 800 richieste al secondo dalla cache delle query ...
BarsMonster

Ok, come ti connetti al database (mysql_pconnect () invece di mysql_connect ())? Usi connessioni persistenti? provare in entrambi i modi ...
Ivan Peevski,

Sono già su pconnect e il pool di connessioni è abilitato in php.ini ...: -S
BarsMonster il

Solo per la saggezza della completezza, proverei a connettermi. Ho visto casi (soprattutto nei test di carico) in cui le prestazioni sono migliori.
Ivan Peevski,

1

Se il collo di bottiglia non è CPU, allora il suo IO - rete o disco. Quindi .. devi vedere quanto IO sta succedendo. Non avrei pensato che fosse la rete (a meno che tu non abbia un collegamento half duplex a 10 Mbps, ma vale la pena controllare lo switch nel caso in cui il rilevamento automatico non funzioni correttamente).

Ciò lascia l'IO del disco, che può essere un fattore importante soprattutto per i VPS. Usa sar o iostat per dare un'occhiata ai dischi, quindi google su come trovare maggiori dettagli se il tuo disco viene utilizzato pesantemente.


Sì, la rete non è il problema: quando si esegue ab dal server locale, le prestazioni sono uguali. Ho controllato il tempo di iowait - è inferiore allo 0,01% - praticamente tutto è nella cache del disco e non ci sono scritture del disco coinvolte nell'elaborazione della richiesta (tutti i registri sono disabilitati).
BarsMonster,

1

Vorrei esaminare la cache con Nginx ( memcached ) o Varnish .

Per lo meno dovresti server di file statici con Nginx come ha detto SaveTheRbtz.


Queste sono pagine dinamiche, quindi preferirei non memorizzarle nella cache.
BarsMonster,

1
memcached non è un'app di memorizzazione nella cache tradizionale e può fare miracoli per le pagine dinamiche. Si trova tra il DB e la tua app. L'app prima interroga memcached per un oggetto, se non è lì, quindi viene caricato dal DB. L'effetto netto è che stai usando la RAM per soddisfare le tue richieste DB piuttosto che l'archiviazione persistente molto più lenta sul DB.
Jamieb,

Memcache può essere utilizzato con nginx, ovvero funzionalità nota. Lo storage persistente più lento non viene utilizzato, è tutto nella cache delle query in MySQL.
BarsMonster,

Memcached e la cache delle query di MySQL non sono realmente comparabili; non fanno nemmeno la stessa cosa. Sei abbastanza veloce da abbattere praticamente ogni suggerimento pubblicato qui senza preoccuparti di capirli. Ti consiglierei di essere un po 'più aperto.
Jamieb,

Comprendo chiaramente la differenza tra cache di query memcached e MySQL. Ma a causa del fatto che tutto è nella cache delle query con un hit ratio del 100%, non lo definirei "archiviazione persistente lenta". La risposta originale di ieri riguardava l'uso di NginX + Memcached, uno scenario abbastanza comune per memorizzare nella cache intere pagine. La memorizzazione nella cache di singoli oggetti è un altro scenario totalmente diverso. Mentre l'utilizzo di memcached davanti a MySQL è sul tavolo, sto pensando di ottenere più succo senza di esso per ora (poiché richiederebbe un bel po 'di modifiche al codice).
BarsMonster,

1

Poiché il server non sembra essere un problema, forse lo è il generatore di carico. Prova a eseguirlo su più macchine.


Le prestazioni sono le stesse anche se la eseguo dal server stesso. Indipendentemente dal modo in cui vengono eseguite connessioni simultanee - 10 o 50. Il test del carico viene
eseguito

1

Mi sembra che tu stia colpendo la massima quantità di connessioni consentite da Apache. Dai un'occhiata alla tua configurazione di Apache. L'aumento del limite del server e dei client massimi dovrebbe aiutare se non si è già vincolati da altri limiti come I / O o memoria. Guarda i valori presenti per mpm_prefork_module o mpm_worker_module e modifica di conseguenza per soddisfare le tue esigenze.

ServerLimit 512
MaxClients 512

Bene, ho davvero bisogno di questo a condizione che io abbia nginx di fronte ad apache2, quindi credo che non ci sia molto senso di avere più di core fisici * 2 processi Apache2 ....
BarsMonster

Ho appena verificato questo. L'aumento del numero di processi Apache2 da 4 a 16 non ha migliorato affatto le prestazioni (è addirittura diminuito dello 0,5%). L'aumento del numero di lavoratori nginx a 2 o 4 non ha migliorato nulla.
BarsMonster,

1
Se i tuoi dati sono abbastanza statici, cioè non aggiornano ogni altro caricamento della pagina, potresti aumentare query_cache. MySQL manterrà il set di risultati in quel modo ed estrarrà dalla memoria. Tuttavia, se la tabella che viene memorizzata nella cache riceve delle scritture durante quel periodo, invalida la cache (anche se i dati non sono interessati), rendendola sprecata di memoria.
Erik Giberti,

In questo momento vedo il tasso di hit della cache delle query al 100% e MySQL è ancora lento ...
BarsMonster

1
Aggiungi skip-name-resolve al tuo file di configurazione di MySQL. Ciò salverà una ricerca DNS su ogni connessione al server. Lo svantaggio qui è che tutte le connessioni dovranno essere bloccate dall'IP (supponendo che tu non stia usando '%'). Se l'SQL si trova sullo stesso server e non è necessario accedervi in ​​un luogo diverso da localhost, è inoltre possibile aggiungere skip-networking per eliminare l'intero stack TCP / IP. Tuttavia, penso che il collo di bottiglia sia Apache.
Erik Giberti,

0

Questo carico è generato da uno strumento o carichi del mondo reale?

Potresti voler controllare memcached. Ho riscontrato problemi ad alte velocità di connessione che causavano latenza nell'applicazione.

Se si utilizza un generatore di carico, cosa si ottiene quando si colpisce una piccola pagina statica?

Durante i carichi, potresti voler controllare lo stack di rete per le condizioni TIME_WAIT. Forse stai riempiendo la coda di connessione.

Ci sono circa un centinaio di ragioni e oggetti che puoi guardare ma senza ulteriori informazioni, sto solo gettando ipotesi a questo punto.


È testato tramite ab-c 10 -t 10 URL che sto confrontando dal server stesso, quindi la rete non dovrebbe essere il problema. Ho pubblicato più benchmark per la tua richiesta.
BarsMonster,

Non spenderei troppo sforzo per sintonizzarmi con un b. Potresti scoprire che non si traduce bene in prestazioni del mondo reale. Quello che potresti voler fare è sezionare la tua app e testare ogni componente. Ad esempio, colpisci direttamente il server apache con una pagina statica molto piccola. Questo ti darà e idea del tuo massimo req / sec sul backend. Metti nginx in primo piano, prova di nuovo chiamando lo stesso file back-end. Quindi prova con una semplice pagina php di tipo "ciao mondo" A volte tutti i livelli possono mascherare qualcosa di semplice. Inoltre, guarda le connessioni durante il test. Assicurarsi che lo stack di rete non si riempia.
jeffatrackaid,

Ho fatto questi benchmark ieri e sono nella descrizione della domanda originale aggiornata. Inoltre, i test vengono eseguiti su localhost, quindi la rete non è un problema.
BarsMonster,

La rete può essere un problema anche se eseguita su un host locale. Probabilmente nel tuo caso, ma può causare problemi. Al momento non hai un limite superiore di ~ 450 req / sec con la tua attuale configurazione di PHP. Il passaggio successivo consiste nel rilasciare una chiamata al database e vedere come cambia. Mi piace dividerlo quando eseguo un'ottimizzazione di alto livello in quanto può davvero aiutarti a individuare il livello che causa la maggior parte dei problemi.
jeffatrackaid,

-1

Il 99% delle volte problemi come questo risalgono al database. Assicurati prima di tutto gli indici di colpire. Se non funziona, inizia a memorizzare nella cache tutto ciò che puoi.


Sono tutti indici e, come dicevo, colpisce persino la cache delle query MySQL nel 100% dei casi
BarsMonster,

-1

Ti consiglio di usare (se possibile) un pool di connessioni per mantenere il database connesso alle tue applicazioni web (non è necessario riconnettersi ad ogni richiesta). Ciò può fare un'enorme differenza di velocità.

Inoltre, prova ad analizzare tutte le tue query con EXPLAIN (e perché non creare un profilo delle tue query con SHOW PROFILE?).


Tutte le query utilizzano gli indici. Viene utilizzato il pool di connessioni MySQL.
BarsMonster,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.