Quels sont les inconvénients de l'utilisation de la connexion persistante dans PDO


184

Dans PDO, une connexion peut être rendue persistante à l'aide de l' PDO::ATTR_PERSISTENTattribut. Selon le manuel php -

Les connexions persistantes ne sont pas fermées à la fin du script, mais sont mises en cache et réutilisées lorsqu'un autre script demande une connexion en utilisant les mêmes informations d'identification. Le cache de connexion persistant vous permet d'éviter la surcharge liée à l'établissement d'une nouvelle connexion chaque fois qu'un script a besoin de parler à une base de données, ce qui se traduit par une application Web plus rapide.

Le manuel recommande également de ne pas utiliser de connexion persistante lors de l'utilisation du pilote ODBC PDO, car cela peut gêner le processus de regroupement de connexions ODBC.

Donc, apparemment, il ne semble y avoir aucun inconvénient à utiliser une connexion persistante dans PDO, sauf dans le dernier cas. Cependant, je voudrais savoir s'il y a d'autres inconvénients à utiliser ce mécanisme, c'est-à-dire une situation où ce mécanisme entraîne une dégradation des performances ou quelque chose du genre.


Wow, vous avez payé une prime de 1000 représentants pour cette simple question?
Pacerier

Réponses:


294

Veuillez lire cette réponse ci - dessous , qui détaille les moyens d'atténuer les problèmes décrits ici.


Les mêmes inconvénients existent en utilisant PDO comme avec toute autre interface de base de données PHP qui effectue des connexions persistantes: si votre script se termine de manière inattendue au milieu des opérations de base de données, la prochaine requête qui obtient la connexion restante reprendra là où le script mort s'est arrêté. La connexion est maintenue ouverte au niveau du gestionnaire de processus (Apache pour mod_php, le processus FastCGI actuel si vous utilisez FastCGI, etc.), pas au niveau PHP, et PHP ne dit pas au processus parent de laisser la connexion mourir lorsque le script se termine anormalement.

Si le script mort a verrouillé des tables, ces tables resteront verrouillées jusqu'à ce que la connexion s'éteigne ou que le prochain script qui obtient la connexion déverrouille les tables lui-même.

Si le script mort était au milieu d'une transaction, cela peut bloquer une multitude de tables jusqu'à ce que le minuteur d'interblocage se déclenche, et même dans ce cas, le minuteur de blocage peut tuer la demande la plus récente au lieu de l'ancienne demande à l'origine du problème.

Si le script mort était au milieu d'une transaction, le script suivant qui obtient cette connexion obtient également l'état de la transaction. Il est très possible (en fonction de la conception de votre application) que le prochain script n'essaie jamais de valider la transaction existante, ou s'engage alors qu'il n'aurait pas dû, ou annule quand il n'aurait pas dû.

C'est la partie visible de l'iceberg. Tout cela peut être atténué dans une certaine mesure en essayant toujours de nettoyer après une connexion sale sur chaque requête de script, mais cela peut être pénible en fonction de la base de données. À moins que vous n'ayez identifié la création de connexions à la base de données comme la seule chose qui constitue un goulot d'étranglement dans votre script (cela signifie que vous avez effectué le profilage de code à l'aide de xdebug et / ou xhprof ), vous ne devriez pas considérer les connexions persistantes comme une solution à quoi que ce soit.

De plus, la plupart des bases de données modernes (y compris PostgreSQL) ont leurs propres méthodes préférées pour effectuer le pool de connexions qui ne présentent pas les inconvénients immédiats des connexions persistantes basées sur PHP.


Pour clarifier un point, nous utilisons des connexions persistantes sur mon lieu de travail, mais pas par choix. Nous rencontrions un comportement de connexion étrange , où la connexion initiale de notre serveur d'application à notre serveur de base de données prenait exactement trois secondes, alors qu'elle aurait dû prendre une fraction de fraction de seconde. Nous pensons que c'est un bogue du noyau. Nous avons renoncé à essayer de le dépanner car cela se produisait au hasard et ne pouvait pas être reproduit à la demande, et notre informatique externalisée n'avait pas la capacité concrète de le localiser.

Quoi qu'il en soit, lorsque les gens de l'entrepôt traitent quelques centaines de pièces entrantes et que chaque pièce prend trois secondes et demie au lieu d'une demi-seconde, nous avons dû agir avant qu'ils ne nous kidnappent tous et nous obligent à les aider. Ainsi, nous avons retourné quelques éléments dans notre monstruosité ERP / CRM / CMS locale et avons expérimenté toutes les horreurs des connexions persistantes de première main. Il nous a fallu des semaines pour dépister tous les petits problèmes subtils et les comportements bizarres qui se sont produits apparemment au hasard. Il s'est avéré que ces erreurs fatales une fois par semaine que nos utilisateurs éliminaient avec diligence de notre application laissaient des tables verrouillées, des transactions abandonnées et d'autres états malheureux et bancaux.

Cette histoire sanglante a un point: elle a cassé des choses que nous ne nous attendions jamais à casser, tout cela au nom de la performance. Le compromis n'en valait pas la peine, et nous attendons avec impatience le jour où nous pourrons revenir aux connexions normales sans émeute de la part de nos utilisateurs.


2
J'espère avoir lu cette réponse avant de courirSELECT orders.* FROM orders LEFT JOIN items USING(item_id)
Ast Derek

31
Je connais un grand site Web qui utilise des connexions persistantes depuis près d'une décennie maintenant. L'astuce consiste à utiliser une couche au-dessus de l'extension DB et à lui faire mémoriser les choses qui doivent être nettoyées à l'aide de register_shutdown_function(). Si le processus meurt, la connexion s'éteint également. Si ce n'est pas le cas, la connexion est réinitialisée à son état propre (par exemple, les transactions ouvertes sont annulées). En cas d'échec, la connexion est fermée et une nouvelle sera ouverte par la prochaine demande au même processus. Il n'est pas nécessaire de diaboliser les connexions persistantes.
Walter Tross

Je suis curieux @Charles ... votre problème a-t-il déjà été résolu?
Tschallacka

@MichaelDibbets Nous avons remplacé le serveur d'application il y a quelques mois, et avons désactivé pconnect pour voir si le bogue de trois secondes était toujours là. Ce n'était pas. Cela a été résolu par procuration, je suppose. La réponse ci-dessous en ce qui concerne mysqli_change_userest probablement la meilleure solution de contournement pour les personnes qui doivent établir des connexions persistantes dans une application non conçue pour traiter des problèmes d'état.
Charles

6
Nous avons eu un délai de 5 secondes à la connexion, que nous avons réussi à isoler en tant que problème DNS + IPv6. Le serveur recherchait une adresse v6, échouant, puis utilisant l'adresse IPv4.
Nigel Atkinson

47

En réponse au problème de Charles ci-dessus,

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

Une plainte courante concernant les connexions persistantes est que leur état n'est pas réinitialisé avant la réutilisation. Par exemple, les transactions ouvertes et non terminées ne sont pas automatiquement annulées. Mais aussi, les modifications d'autorisation qui se sont produites entre la mise de la connexion dans le pool et sa réutilisation ne sont pas reflétées. Cela peut être considéré comme un effet secondaire indésirable. Au contraire, le nom persistant peut être compris comme une promesse que l'état est persistant.

L'extension mysqli prend en charge les deux interprétations d'une connexion persistante: état persistant et état réinitialisé avant réutilisation. La valeur par défaut est réinitialisée. Avant qu'une connexion persistante ne soit réutilisée, l'extension mysqli appelle implicitement mysqli_change_user()pour réinitialiser l'état. La connexion persistante apparaît à l'utilisateur comme si elle venait juste d'être ouverte. Aucun artefact des utilisations précédentes n'est visible.

La mysqli_change_user()fonction est une opération coûteuse. Pour de meilleures performances, les utilisateurs peuvent souhaiter recompiler l'extension avec l'indicateur de compilation MYSQLI_NO_CHANGE_USER_ON_PCONNECTdéfini.

Il appartient à l'utilisateur de choisir entre un comportement sûr et des performances optimales. Les deux sont des objectifs d'optimisation valides. Pour faciliter l'utilisation, le comportement sûr a été défini par défaut au détriment des performances maximales.


+1, sinon pour le fait que nous avons nettoyé le désordre d'une autre manière, j'aimerais voir si l' appel manuel de change_user aurait résolu nos problèmes d'états inconnus bizarres.
Charles

Quel est l'équivalent des connexions persistantes PDO Postgres? J'ai des problèmes similaires comme @Charles avait, où après un certain temps les utilisateurs recevraient une erreur comme fetch sql - le serveur a fermé la connexion de manière inattendue Cela signifie probablement que le serveur s'est arrêté anormalement lors de l'exécution d'une simple requête SELECT (pas même des transactions).
Carmageddon

1
@Carmageddon, c'est plus adapté à une nouvelle question, mais le tl; dr est que Postgres ne fait pas pconnect et que vous devriez utiliser l'un des pools de connexions externes à la place.
Charles

@Charles, que voulez-vous dire par là? l'utilisation de la connexion persistante du PDO n'est-elle pas équivalente à l'utilisation de "pools de connexions externes"? ou que vouliez-vous dire?
Carmageddon

@Carmageddon, ce que je veux dire, c'est que la communauté Postgres a choisi le pool de connexions comme une meilleure solution que pconnect. Découvrez pgbouncer ou pgpool-II. Je ne suis pas sûr que PDO fasse de toute façon Postgres pconnect, mais je suis peut-être totalement hors de ma bascule.
Charles

13

Les connexions persistantes ne sont une bonne idée que lorsque la connexion à votre base de données prend un temps (relativement) long. De nos jours, ce n'est presque jamais le cas. Le plus gros inconvénient des connexions persistantes est que cela limite le nombre d'utilisateurs que vous pouvez avoir sur votre site: si MySQL est configuré pour n'autoriser que 10 connexions simultanées à la fois, lorsqu'une 11ème personne essaie de naviguer sur votre site, cela ne fonctionnera pas pour eux. .

PDO ne gère pas la persistance. Le pilote MySQL le fait. Il réutilise les connexions quand a) elles sont disponibles et que l'hôte / l'utilisateur / le mot de passe / la base de données correspondent. En cas de changement, il ne réutilisera pas une connexion. Le meilleur effet net des cas est que ces connexions que vous avez seront démarrées et arrêtées si souvent parce que vous avez différents utilisateurs sur le site et que les rendre persistants ne sert à rien.

La chose clé à comprendre à propos des connexions persistantes est que vous ne devez PAS les utiliser dans la plupart des applications Web. Ils semblent séduisants mais ils sont dangereux et pratiquement inutiles.

Je suis sûr qu'il existe d'autres discussions à ce sujet, mais une connexion persistante est dangereuse car elle persiste entre les demandes. Si, par exemple, vous verrouillez une table pendant une demande et que vous ne parvenez pas à la déverrouiller, cette table restera verrouillée indéfiniment. Les connexions persistantes sont également à peu près inutiles pour 99% de vos applications car vous n'avez aucun moyen de savoir si la même connexion sera utilisée entre différentes requêtes. Chaque thread Web aura son propre ensemble de connexions persistantes et vous n'avez aucun moyen de contrôler quel thread gérera quelles requêtes.

La bibliothèque procédurale mysql de PHP, a une fonctionnalité par laquelle les appels ultérieurs à mysql_connect renverront le même lien, plutôt que d'ouvrir une connexion différente (comme on pourrait s'y attendre). Cela n'a rien à voir avec les connexions persistantes et est spécifique à la bibliothèque mysql. AOP ne présente pas un tel comportement


Lien de ressource: lien

En général, vous pouvez l'utiliser comme un "ensemble de règles" approximatif:

OUI , utilisez des connexions persistantes, si:

  • Il n'y a que quelques applications / utilisateurs accédant à la base de données, c'est-à-dire que vous n'obtiendrez pas 200 connexions ouvertes (mais probablement inactives), car il y a 200 utilisateurs différents partagés sur le même hôte.
  • La base de données s'exécute sur un autre serveur auquel vous accédez via le réseau

  • Une (une) application accède très souvent à la base de données

NON , n'utilisez pas de connexions persistantes, si:

  • Votre application n'a besoin d'accéder à la base de données que 100 fois par heure.

  • Vous avez de très nombreux serveurs Web accédant à un serveur de base de données

L'utilisation de connexions persistantes est beaucoup plus rapide, surtout si vous accédez à la base de données via un réseau. Cela ne fait pas tellement de différence si la base de données s'exécute sur la même machine, mais c'est quand même un peu plus rapide. Cependant - comme son nom l'indique - la connexion est persistante, c'est-à-dire qu'elle reste ouverte, même si elle n'est pas utilisée.

Le problème avec cela est que dans la "configuration par défaut", MySQL n'autorise que 1000 "canaux ouverts" parallèles. Après cela, les nouvelles connexions sont refusées (vous pouvez modifier ce paramètre). Donc, si vous avez - disons - 20 serveurs Web avec chacun 100 clients sur eux, et chacun d'entre eux n'a qu'un accès à une page par heure, des calculs simples vous montreront que vous aurez besoin de 2000 connexions parallèles à la base de données. Cela ne fonctionnera pas.

Ergo: utilisez-le uniquement pour les applications avec beaucoup de demandes.


4
Après la ligne, votre réponse est un copier-coller de stackoverflow.com/a/51583/718224
Tony Stark

1
"OUI, utilisez des connexions persistantes, si: [...] Il n'y a que peu d'applications / d'utilisateurs accédant à la base de données" est en contradiction avec "Ne l'utilisez que pour les applications avec beaucoup de requêtes.". Ce dernier est cependant correct. Situation: des milliers de requêtes par seconde entraîneront des centaines de connexions de base de données actives. Lorsqu'un système évolue de manière linéaire, il met également à l'échelle de manière linéaire le nombre de connexions à la base de données. Ainsi, plus de demandes (plus d'utilisateurs) entraîneront plus de connexions. Vous avez donc besoin de connexions actives limitées (!) Mais nombreuses lorsque vous avez beaucoup de demandes (utilisateurs)
Ken Van Hoeylandt

12

Lors de mes tests, j'ai eu un temps de connexion de plus d'une seconde à mon hôte local, supposant ainsi que je devrais utiliser une connexion persistante. D'autres tests ont montré qu'il s'agissait d'un problème avec 'localhost':

Résultats des tests en secondes (mesurés par microtime php):

  • Web hébergé: connectDB: 0,0038912296295166
  • localhost: connectDB: 1.0214691162109 (sur une seconde: n'utilisez pas localhost!)
  • 127.0.0.1: connectDB: 0.00097203254699707

Fait intéressant: le code suivant est aussi rapide que l'utilisation de 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));

On dirait que PDO a du mal à traduire les noms de domaine! Merci, je me demandais pourquoi chaque connexion prenait sacrément longtemps sur ma machine quad core!
Mustafa

@Gunnar Bernstein +1 belle trouvaille. "localhost" prend certainement plus de temps et cela a quelque peu amélioré la vitesse de mon application Web (cela crée beaucoup de connexions).
imperium2335

1
C'est bien. Quelque chose ne va pas avec la résolution de ma machine de développement ... l'utilisation d'une adresse IP a fait passer mon script de 6.1s à 1.1s
Pete

localhostutilise une connexion socket, la connexion socket est connue pour être mauvaise sur une grande quantité de connexions
mente

@mente Une référence, une ressource qui peut prouver ce fait? J'aurais tendance à penser que l'UDS est préférable à TCP. Merci.
Nuxwin

6

Les connexions persistantes devraient augmenter considérablement les performances. Je ne suis pas d'accord avec l'affirmation selon laquelle vous devriez «éviter» la persévérance.

On dirait que les plaintes ci-dessus sont conduites par quelqu'un utilisant des tables MyIASM et piratant leurs propres versions de transactions en saisissant des verrous de table .. Bien sûr, vous allez dans une impasse! Utilisez beginTransaction () de PDO et déplacez vos tables vers InnoDB.


2
Un an de retard, je me rends compte, mais pour mémoire: mon histoire provient d'une base de données entièrement composée de tables InnoDB, à la seule exception d'une poignée de clones dénormalisés coincés dans le bourbier de MyISAM pour le support de l'indexation de texte intégral.
Charles

Pfft, Sphinx est vieux et cassé, ElasticSearch est le nouveau hotness. Un beau jour, nous l'utiliserons pour nos anciennes applications au lieu de juste les nouvelles ...
Charles

La recherche plein texte dans PostgreSQL est le vrai gagnant. C'est incroyable. Ne nécessite pas un autre outil / serveur en cours d'exécution pour faire son travail. N'a pas à s'inquiéter de la synchronisation des données. Contrôles très granulaires. Plusieurs dictionnaires ou rédigez le vôtre. Et comme PostgreSQL utilise automatiquement des requêtes multi-index, vous pouvez simplement le déposer avec n'importe quelle autre requête que vous exécutez.
brightball

2
MySQL 5.6 offre une prise en charge du texte intégral pour les tables InnoDB.
horaire du

2

me semble avoir une connexion persistante consommerait plus de ressources système. Peut-être un montant insignifiant, mais quand même ...


Souvent, un échange de beaucoup de temps humain pour des microsecondes de temps informatique
Andy Chase

1

L'explication de l'utilisation de connexions persistantes est évidemment de réduire la quantité de connexions qui sont plutôt coûteuses, malgré le fait qu'elles sont considérablement plus rapides avec MySQL par rapport aux autres bases de données.

Le tout premier problème avec les connexions persistantes ...

Si vous créez des milliers de connexions par seconde, vous ne vous assurez normalement pas qu'il reste ouvert pendant très longtemps, contrairement au système d'exploitation. Basé sur le protocole TCP / IP Les ports ne peuvent pas être recyclés instantanément et doivent également investir un certain temps dans l'étape «FIN» en attendant qu'ils puissent être recyclés.

Le 2ème problème ... en utilisant beaucoup de connexions serveur MySQL.

Beaucoup de gens ne réalisent tout simplement pas que vous êtes capable d'augmenter la variable * max_connections * et d'obtenir plus de 100 connexions simultanées avec MySQL, d'autres ont été battus par d'anciens problèmes Linux de l'incapacité de transmettre plus de 1024 connexions avec MySQL.

Permet de parler maintenant des raisons pour lesquelles les connexions persistantes ont été désactivées dans l'extension mysqli. Malgré le fait que vous pouvez abuser des connexions persistantes et obtenir de mauvaises performances, ce qui n'était pas la raison principale. La vraie raison est que vous pouvez avoir beaucoup plus de problèmes avec cela.

Des connexions persistantes ont été mises en PHP à travers MySQL 3.22 / 3.23 lorsque MySQL n'était pas si difficile, ce qui signifie que vous pouvez facilement recycler les connexions sans problème. Cependant, dans les versions ultérieures, une quantité de problèmes est survenue - Si vous recyclez une connexion qui a des transactions non validées, vous rencontrez des problèmes. Si vous recyclez les connexions avec des configurations de jeu de caractères personnalisées, vous êtes à nouveau en danger, ainsi que les variables éventuellement transformées par session.

Un problème avec l'utilisation de connexions persistantes est qu'elle ne s'adapte pas vraiment bien. Pour ceux qui ont 5000 personnes connectées, vous aurez besoin de 5000 connexions persistantes. En dehors de l'exigence de persévérance, vous pouvez avoir la possibilité de servir 10000 personnes avec un nombre similaire de connexions, car elles sont en mesure de partager des connexions individuelles lorsqu'elles ne sont pas avec elles.


0

Je me demandais simplement si une solution partielle serait d'avoir un pool de connexions à usage unique. Vous pouvez passer du temps à créer un pool de connexions lorsque le système est à faible utilisation, jusqu'à une limite, les distribuer et les tuer lorsqu'ils sont terminés ou expirés. En arrière-plan, vous créez de nouvelles connexions au fur et à mesure qu'elles sont prises. Dans le pire des cas, cela devrait être aussi lent que la création de la connexion sans le pool, en supposant que l'établissement du lien est le facteur limitant?

En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.