Quali sono gli svantaggi dell'utilizzo della connessione persistente in DOP


181

In DOP, è possibile rendere persistente una connessione utilizzando l' PDO::ATTR_PERSISTENTattributo. Secondo il manuale di php -

Le connessioni permanenti non vengono chiuse alla fine dello script, ma vengono memorizzate nella cache e riutilizzate quando un altro script richiede una connessione utilizzando le stesse credenziali. La cache di connessione persistente consente di evitare il sovraccarico di stabilire una nuova connessione ogni volta che uno script deve parlare con un database, risultando in un'applicazione Web più veloce.

Il manuale raccomanda inoltre di non utilizzare la connessione persistente durante l'utilizzo del driver ODBC PDO, poiché potrebbe ostacolare il processo di pooling di connessioni ODBC.

Quindi, a quanto pare, non sembrano esserci svantaggi nell'uso della connessione persistente in DOP, tranne nell'ultimo caso. Tuttavia, vorrei sapere se ci sono altri svantaggi nell'utilizzare questo meccanismo, vale a dire una situazione in cui questo meccanismo comporta un degrado delle prestazioni o qualcosa del genere.


Wow, hai pagato una taglia di 1000 rappresentanti per questa semplice domanda?
Pacerier,

@Pacerier, no, era qualcun altro .
Charles

Risposte:


287

Assicurati di leggere questa risposta di seguito , che descrive i modi per mitigare i problemi descritti qui.


Gli stessi svantaggi esistono usando PDO come con qualsiasi altra interfaccia di database PHP che esegue connessioni persistenti: se lo script termina inaspettatamente nel mezzo delle operazioni del database, la richiesta successiva che ottiene la connessione rimanente riprenderà da dove lo script morto era stato interrotto. La connessione viene mantenuta aperta a livello del gestore processi (Apache per mod_php, l'attuale processo FastCGI se si utilizza FastCGI, ecc.), Non a livello di PHP, e PHP non dice al processo padre di interrompere la connessione quando lo script termina in modo anomalo.

Se lo script morto ha bloccato le tabelle, quelle tabelle rimarranno bloccate fino a quando la connessione non si interrompe o lo script successivo che ottiene la connessione sblocca le tabelle stesse.

Se lo script dead si trovava nel mezzo di una transazione, ciò può bloccare una moltitudine di tabelle fino a quando non scatta il timer deadlock, e anche in questo caso, il timer deadlock può uccidere la richiesta più recente invece della richiesta precedente che causa il problema.

Se lo script morto si trovava nel mezzo di una transazione, anche lo script successivo che ottiene quella connessione ottiene lo stato della transazione. È molto possibile (a seconda del progetto dell'applicazione) che lo script successivo in realtà non provi mai a eseguire il commit della transazione esistente o commetterà quando non dovrebbe avere o eseguirà il rollback quando non dovrebbe.

Questa è solo la punta dell'iceberg. Può essere mitigato in una certa misura provando sempre a ripulire dopo una connessione sporca su ogni singola richiesta di script, ma ciò può essere una seccatura a seconda del database. A meno che non avete individuato la creazione di connessioni al database come l'unica cosa che è un collo di bottiglia nello script (questo significa che hai fatto il codice profiling utilizzando xdebug e / o xhprof ), si dovrebbe non prendere in considerazione le connessioni persistenti come una soluzione per qualsiasi cosa.

Inoltre, i database più moderni (incluso PostgreSQL) hanno i loro modi preferiti di eseguire il pool di connessioni che non hanno gli svantaggi immediati delle semplici connessioni persistenti basate su PHP.


Per chiarire un punto, usiamo connessioni persistenti sul mio posto di lavoro, ma non per scelta. Stavamo riscontrando uno strano comportamento di connessione, in cui la connessione iniziale dal nostro server delle applicazioni al nostro server di database impiegava esattamente tre secondi, quando avrebbe dovuto impiegare una frazione di una frazione di secondo. Pensiamo che sia un bug del kernel. Abbiamo rinunciato a cercare di risolverlo perché si è verificato in modo casuale e non è stato possibile riprodurlo su richiesta e il nostro IT esternalizzato non ha avuto la capacità concreta di rintracciarlo.

Indipendentemente da ciò, quando le persone nel magazzino stanno elaborando alcune centinaia di parti in arrivo, e ogni parte impiega tre secondi e mezzo invece di mezzo secondo, abbiamo dovuto agire prima che ci rapissero tutti e ci facessero aiutare. Quindi, abbiamo lanciato alcuni bit nella nostra mostruosità ERP / CRM / CMS cresciuta in casa e abbiamo sperimentato di persona tutti gli orrori delle connessioni persistenti. Ci sono volute settimane per rintracciare tutti i piccoli problemi sottili e i comportamenti bizzarri che sembravano essere casuali. Si è scoperto che quegli errori fatali una volta alla settimana che i nostri utenti hanno diligentemente spremuto dalla nostra app stavano lasciando tavoli bloccati, transazioni abbandonate e altri sfortunati stati traballanti.

Questo sob-story ha un punto: ha rotto cose che non ci saremmo mai aspettati di rompere, tutto in nome della performance. Il compromesso non è valsa la pena, e stiamo aspettando con impazienza il giorno in cui potremo tornare alle normali connessioni senza una rivolta dei nostri utenti.


2
Spero di aver letto questa risposta prima di correreSELECT orders.* FROM orders LEFT JOIN items USING(item_id)
Ast Derek il

31
Conosco un grande sito Web che utilizza connessioni persistenti da quasi un decennio ormai. Il trucco sta usando un livello sopra l'estensione del DB, e ricordando le cose che devono essere ripulite usando register_shutdown_function(). Se il processo termina, anche la connessione muore. In caso contrario, la connessione viene ripristinata allo stato pulito (ad esempio, le transazioni aperte vengono ripristinate). In caso contrario, la connessione verrà chiusa e una nuova verrà aperta dalla successiva richiesta allo stesso processo. Non è necessario demonizzare le connessioni persistenti.
Walter Tross,

Sono curioso @Charles ... il tuo problema è mai stato risolto?
Tschallacka,

@MichaelDibbets Alcuni mesi fa abbiamo sostituito il server delle applicazioni e abbiamo disattivato pconnect per vedere se il bug di tre secondi era ancora presente. Non lo era. È stato risolto dal proxy, suppongo. La risposta di seguito per quanto riguarda mysqli_change_userè probabilmente la soluzione alternativa migliore per le persone che devono effettuare connessioni persistenti in un'applicazione non progettata per gestire i problemi di stato.
Charles,

5
Abbiamo avuto un ritardo di 5 secondi sulla connessione, che siamo riusciti a isolare come problema DNS + IPv6. Il server stava cercando un indirizzo v6, non funzionava e quindi utilizzava l'indirizzo IPv4.
Nigel Atkinson,

45

In risposta al problema di Charles sopra,

Da: http://www.php.net/manual/en/mysqli.quickstart.connections.php -

Una lamentela comune riguardo alle connessioni persistenti è che il loro stato non viene ripristinato prima del riutilizzo. Ad esempio, le transazioni aperte e non finite non vengono automaticamente ripristinate. Inoltre, le modifiche all'autorizzazione avvenute nel tempo tra l'inserimento della connessione nel pool e il suo riutilizzo non vengono riflesse. Questo può essere visto come un effetto collaterale indesiderato. Al contrario, il nome persistente può essere inteso come una promessa che lo stato è persistente.

L'estensione mysqli supporta entrambe le interpretazioni di una connessione persistente: stato persistente e stato ripristinato prima del riutilizzo. L'impostazione predefinita è ripristinata. Prima di riutilizzare una connessione persistente, l'estensione mysqli chiama implicitamente mysqli_change_user()per ripristinare lo stato. La connessione persistente appare all'utente come se fosse appena stata aperta. Non sono visibili artefatti degli usi precedenti.

La mysqli_change_user()funzione è un'operazione costosa. Per prestazioni ottimali, gli utenti potrebbero voler ricompilare l'estensione con il flag di compilazione MYSQLI_NO_CHANGE_USER_ON_PCONNECTimpostato.

È lasciato all'utente la scelta tra comportamento sicuro e prestazioni ottimali. Entrambi sono obiettivi di ottimizzazione validi. Per facilità d'uso, il comportamento sicuro è stato impostato come predefinito a scapito delle massime prestazioni.


+1, se non fosse per il fatto che abbiamo ripulito il casino in altri modi, mi piacerebbe vedere se chiamare manualmente change_user avrebbe risolto i nostri bizzarri problemi di stato sconosciuto.
Charles,

Qual è l'equivalente per le connessioni persistenti Postgres DOP? Ho problemi simili come quelli di @Charles, in cui dopo un po 'gli utenti avrebbero ricevuto errori come recuperare sql - il server ha chiuso la connessione in modo imprevisto Ciò probabilmente significa che il server è stato chiuso in modo anomalo Quando si esegue una semplice query SELECT (nemmeno le transazioni).
Carmageddon,

1
@Carmageddon, è più adatto a una nuova domanda, ma il tl; dr è che Postgres non fa il pconnect e dovresti invece usare uno dei pool di connessione esterni.
Charles,

@Charles, cosa intendi con questo? l'utilizzo della connessione persistente del PDO non equivale all'utilizzo di "pool di connessioni esterne"? o cosa volevi dire?
Carmageddon,

@Carmageddon, intendo dire che la comunità di Postgres ha optato per il pool di connessioni come soluzione migliore di pconnect. Dai un'occhiata a pgbouncer o pgpool-II. Non sono sicuro che il PDO Postgres sia collegato in ogni caso, ma potrei essere totalmente fuori gioco.
Charles

13

Le connessioni permanenti sono una buona idea solo quando ci vuole un tempo (relativamente) lungo per connettersi al database. Oggi non è quasi mai così. Il più grande svantaggio delle connessioni permanenti è che limita il numero di utenti che puoi avere navigando nel tuo sito: se MySQL è configurato per consentire solo 10 connessioni simultanee contemporaneamente, quando un'undicesima persona cerca di navigare nel tuo sito non funzionerà per loro .

DOP non gestisce la persistenza. Lo fa il driver MySQL. Riutilizza le connessioni quando a) sono disponibili e l'host / utente / password / database corrispondono. In caso di modifiche, non verrà riutilizzata una connessione. Il migliore effetto netto è che queste connessioni che hai sono avviate e interrotte così spesso perché hai diversi utenti sul sito e renderli persistenti non fa nulla di buono.

La cosa chiave da capire sulle connessioni permanenti è che NON dovresti usarle nella maggior parte delle applicazioni web. Sembrano allettanti ma sono pericolosi e praticamente inutili.

Sono sicuro che ci sono altri thread su questo, ma una connessione persistente è pericolosa perché persiste tra le richieste. Se, ad esempio, si blocca una tabella durante una richiesta e non si riesce a sbloccare, la tabella rimarrà bloccata a tempo indeterminato. Anche le connessioni persistenti sono praticamente inutili per il 99% delle tue app perché non hai modo di sapere se la stessa connessione verrà utilizzata tra richieste diverse. Ogni thread web avrà il proprio set di connessioni permanenti e non hai modo di controllare quale thread gestirà quali richieste.

La libreria procedurale mysql di PHP, ha una funzione in base alla quale le chiamate successive a mysql_connect restituiranno lo stesso collegamento, anziché aprire una connessione diversa (come ci si potrebbe aspettare). Questo non ha nulla a che fare con connessioni persistenti ed è specifico della libreria mysql. La DOP non presenta questo comportamento


Collegamento risorsa: collegamento

In generale, potresti usarlo come un "set di regole" approssimativo:

, utilizzare connessioni persistenti, se:

  • Esistono solo poche applicazioni / utenti che accedono al database, vale a dire che non si otterranno 200 connessioni aperte (ma probabilmente inattive), perché ci sono 200 utenti diversi condivisi sullo stesso host.
  • Il database è in esecuzione su un altro server a cui si accede tramite la rete

  • Un'applicazione (una) accede al database molto spesso

NO , non utilizzare connessioni persistenti, se:

  • L'applicazione deve solo accedere al database 100 volte all'ora.

  • Hai molti, molti server web che accedono a un server di database

L'uso di connessioni permanenti è molto più veloce, specialmente se si accede al database su una rete. Non fa molta differenza se il database è in esecuzione sullo stesso computer, ma è ancora un po 'più veloce. Tuttavia - come dice il nome - la connessione è persistente, cioè rimane aperta, anche se non viene utilizzata.

Il problema è che, in "configurazione predefinita", MySQL consente solo 1000 "canali aperti" paralleli. Successivamente, le nuove connessioni vengono rifiutate (è possibile modificare questa impostazione). Quindi, se hai - diciamo - 20 server web con ogni 100 client su di essi, e ognuno di essi ha un solo accesso alla pagina all'ora, la semplice matematica ti mostrerà che avrai bisogno di 2000 connessioni parallele al database. Non funzionerà

Ergo: usalo solo per applicazioni con molte richieste.


4
Dopo la riga la tua risposta è copia incolla da stackoverflow.com/a/51583/718224
Tony Stark

1
"SÌ, usa connessioni persistenti, se: [...] Ci sono solo poche applicazioni / utenti che accedono al database" è in contraddizione con "Utilizzalo solo per applicazioni con molte richieste.". Quest'ultimo è tuttavia corretto. Situazione: migliaia di richieste al secondo comporteranno centinaia di connessioni al database attive. Quando un sistema si ridimensiona in modo lineare, scalerà anche in modo lineare la quantità di connessioni al database. Quindi più richieste (più utenti) comporteranno più connessioni. Quindi hai bisogno di molte connessioni attive (!)
Limitate

12

Nei miei test ho avuto un tempo di connessione di oltre un secondo al mio localhost, supponendo quindi che avrei dovuto usare una connessione persistente. Ulteriori test hanno dimostrato che si trattava di un problema con "localhost":

Risultati del test in secondi (misurati con microtime php):

  • Web ospitato: connectDB: 0,0038912296295166
  • localhost: connectDB: 1.0214691162109 (oltre un secondo: non usare localhost!)
  • 127.0.0.1: connectDB: 0.00097203254699707

È interessante notare che il seguente codice è veloce quanto l'utilizzo di 127.0.0.1:

$host = gethostbyname('localhost');
// echo "<p>$host</p>";
$db = new PDO("mysql:host=$host;dbname=" . DATABASE . ';charset=utf8', $username, $password,
    array(PDO::ATTR_EMULATE_PREPARES => false,
    PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION));

Sembra che DOP abbia difficoltà a tradurre i nomi di dominio! Grazie, mi chiedevo perché ogni connessione impiegasse molto tempo sulla mia macchina quad core!
Mustafa,

@Gunnar Bernstein +1 bella scoperta. "localhost" richiede sicuramente più tempo e questo ha migliorato un po 'la velocità della mia app Web (crea molte connessioni).
imperium2335,

1
Questo è fantastico Qualcosa non va con la risoluzione sulla mia macchina di sviluppo ... l'uso di un IP ha portato il mio script da 6.1s a 1.1s
Pete

localhostusa la connessione socket, la connessione socket è famosa per essere cattiva su una grande quantità di connessioni
mente

@mente Qualche riferimento, risorsa in grado di provare questo fatto? Tenderei a pensare che gli UDS siano preferiti al TCP. Grazie.
Nuxwin,

6

Le connessioni persistenti dovrebbero dare un notevole aumento delle prestazioni. Non sono d'accordo con l'orientamento secondo cui dovresti "evitare" la persistenza.

Sembra che le lamentele di cui sopra siano guidate da qualcuno che utilizza le tabelle MyIASM e l'hacking nelle proprie versioni di transazioni afferrando le serrature dei tavoli .. Beh, certo che stai andando a un punto morto! Usa beginTransaction () di PDO e sposta i tuoi tavoli su InnoDB ..


2
Un anno di ritardo, mi rendo conto, ma per la cronaca: la mia storia viene da un database composto interamente da tabelle InnoDB, con la sola eccezione di una manciata di cloni denormalizzati bloccati nel pantano di MyISAM per il supporto dell'indicizzazione del testo completo.
Charles

Pfft, Sphinx è vecchio e sballato, ElasticSearch è il nuovo entusiasmo. Un bel giorno, lo useremo effettivamente per le nostre vecchie app anziché solo per quelle nuove ...
Charles

La ricerca full-text in PostgreSQL è il vero vincitore. È fantastico. Non richiede un altro strumento / server in esecuzione per fare il suo lavoro. Non deve preoccuparsi di mantenere sincronizzati i dati. Controlli molto granulari. Più dizionari o scrivi il tuo. E poiché PostgreSQL utilizza automaticamente query multiindice, puoi semplicemente inserirle con qualsiasi altra query in esecuzione.
Brightball,

2
MySQL 5.6 offre supporto full-text per le tabelle InnoDB.
agenda

2

mi sembra che avere una connessione persistente consumerebbe più risorse di sistema. Forse una quantità banale, ma comunque ...


Spesso uno scambio di tempo umano per microsecondi di tempo al computer
Andy Chase,

1

La spiegazione per l'utilizzo di connessioni permanenti sta ovviamente riducendo la quantità di connessioni che sono piuttosto costose, nonostante siano notevolmente più veloci con MySQL rispetto ad altri database.

Il primo problema con connessioni persistenti ...

Se stai creando migliaia di connessioni al secondo, di solito non ti assicuri che rimanga aperto per molto tempo, ma Operation System lo fa. Basato sul protocollo TCP / IP Le porte non possono essere riciclate all'istante e devono anche investire un po 'di tempo nella fase "FIN" in attesa di poter essere riciclate.

Il secondo problema ... utilizzando molte connessioni al server MySQL.

Molte persone semplicemente non si rendono conto che sei in grado di aumentare la variabile * max_connections * e ottenere oltre 100 connessioni simultanee con MySQL, altre sono state battute da vecchi problemi di Linux sull'incapacità di trasmettere più di 1024 connessioni con MySQL.

Permette ora di parlare del perché le connessioni persistenti sono state disabilitate nell'estensione mysqli. Nonostante il fatto che sia possibile utilizzare in modo improprio le connessioni persistenti e ottenere prestazioni scadenti che non era il motivo principale. Il vero motivo è che puoi ottenere molti più problemi.

Le connessioni persistenti sono state inserite in PHP durante le occasioni di MySQL 3.22 / 3.23 quando MySQL non era così difficile, il che significa che è possibile riciclare facilmente le connessioni senza problemi. Nelle versioni successive, tuttavia, si sono verificati numerosi problemi: se si ricicla una connessione con transazioni non impegnate, si verificano problemi. Se ricicli le connessioni con configurazioni di set di caratteri personalizzati sei di nuovo in pericolo, oltre a possibili trasformazioni per variabili di sessione.

Un problema con l'utilizzo di connessioni persistenti è che non si adatta così bene. Per coloro che hanno 5000 persone connesse, avrai bisogno di 5000 connessioni permanenti. Per via del requisito di persistenza, potresti avere la possibilità di servire 10000 persone con un numero simile di connessioni perché sono in grado di condividere connessioni individuali quando non sono con loro.


0

Mi stavo solo chiedendo se una soluzione parziale sarebbe quella di avere un pool di connessioni use-once. Potresti dedicare tempo alla creazione di un pool di connessioni quando il sistema è a basso utilizzo, fino a un limite, distribuirle e ucciderle quando sono state completate o scadute. Sullo sfondo stai creando nuove connessioni mentre vengono prese. Nel peggiore dei casi, ciò dovrebbe essere lento quanto la creazione della connessione senza il pool, supponendo che stabilire il collegamento sia il fattore limitante?

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.