¿Cuáles son las desventajas de usar una conexión persistente en PDO?

181

En PDO, una conexión se puede hacer persistente utilizando el PDO::ATTR_PERSISTENTatributo De acuerdo con el manual de php -

Las conexiones persistentes no se cierran al final del script, pero se almacenan en caché y se reutilizan cuando otro script solicita una conexión con las mismas credenciales. El caché de conexión persistente le permite evitar la sobrecarga de establecer una nueva conexión cada vez que un script necesita comunicarse con una base de datos, lo que resulta en una aplicación web más rápida.

El manual también recomienda no usar una conexión persistente mientras se usa el controlador PDO ODBC, ya que puede obstaculizar el proceso de agrupación de conexiones ODBC.

Así que aparentemente no parece haber inconvenientes en el uso de la conexión persistente en PDO, excepto en el último caso. Sin embargo, me gustaría saber si hay otras desventajas de usar este mecanismo, es decir, una situación en la que este mecanismo da como resultado una degradación del rendimiento o algo así.

MD Sayem Ahmed
fuente
Wow, ¿pagaste 1000 recompensas por esta simple pregunta?
Pacerier
@Pacerier, no, era otra persona .
Charles

Respuestas:

287

Asegúrese de leer esta respuesta a continuación , que detalla las formas de mitigar los problemas descritos aquí.


Existen los mismos inconvenientes con PDO que con cualquier otra interfaz de base de datos PHP que realice conexiones persistentes: si su script finaliza inesperadamente en medio de las operaciones de la base de datos, la siguiente solicitud que obtiene la conexión sobrante continuará donde quedó el script muerto. La conexión se mantiene abierta en el nivel del administrador de procesos (Apache para mod_php, el proceso actual FastCGI si está usando FastCGI, etc.), no en el nivel PHP, y PHP no le dice al proceso padre que deje que la conexión falle cuando el script termina de manera anormal.

Si el script muerto bloqueó las tablas, esas tablas permanecerán bloqueadas hasta que la conexión falle o el siguiente script que obtenga la conexión desbloquee las tablas en sí.

Si el script inactivo se encontraba en medio de una transacción, eso puede bloquear una multitud de tablas hasta que se active el temporizador de interbloqueo, e incluso entonces, el temporizador de interbloqueo puede eliminar la solicitud más nueva en lugar de la solicitud anterior que está causando el problema.

Si el script muerto estaba en medio de una transacción, el siguiente script que obtiene esa conexión también obtiene el estado de la transacción. Es muy posible (dependiendo del diseño de su aplicación) que el próximo script no intente realmente confirmar la transacción existente, o se comprometerá cuando no debería hacerlo, o retroceder cuando no debería.

Esta es solo la punta del iceberg. Todo se puede mitigar hasta cierto punto siempre tratando de limpiar después de una conexión sucia en cada solicitud de script, pero eso puede ser un problema dependiendo de la base de datos. A menos que haya identificado la creación de conexiones de bases de datos como la única cosa que es un cuello de botella en el script (esto significa que has hecho de perfiles de código usando Xdebug y / o xhprof ), debería no tener en cuenta las conexiones persistentes como una solución a nada.

Además, la mayoría de las bases de datos modernas (incluyendo PostgreSQL) tienen sus propias formas preferidas de realizar agrupaciones de conexiones que no tienen los inconvenientes inmediatos que tienen las conexiones persistentes basadas en PHP.


Para aclarar un punto, utilizamos conexiones persistentes en mi lugar de trabajo, pero no por elección. Nos encontramos con un comportamiento de conexión extraño , donde la conexión inicial de nuestro servidor de aplicaciones a nuestro servidor de base de datos tardaba exactamente tres segundos, cuando debería haber tomado una fracción de fracción de segundo. Creemos que es un error del kernel. Dejamos de tratar de solucionarlo porque sucedió al azar y no se podía reproducir a pedido, y nuestra TI tercerizada no tenía la capacidad concreta de rastrearlo.

En cualquier caso, cuando la gente del almacén procesa unos cientos de piezas entrantes, y cada parte tarda tres segundos y medio en lugar de medio segundo, tuvimos que tomar medidas antes de que nos secuestraran a todos y nos hicieran ayudarlos. Entonces, encendimos algunos bits en nuestra monstruosidad ERP / CRM / CMS local y experimentamos todos los horrores de las conexiones persistentes de primera mano. Nos llevó semanas rastrear todos los pequeños problemas sutiles y el comportamiento extraño que sucedió aparentemente al azar. Resultó que esos errores fatales de una vez a la semana que nuestros usuarios exprimían diligentemente de nuestra aplicación estaban dejando mesas bloqueadas, transacciones abandonadas y otros desafortunados estados inestables.

Esta historia de sollozo tiene un punto: rompió cosas que nunca esperábamos romper, todo en nombre del rendimiento. La compensación no valió la pena, y estamos esperando ansiosamente el día en que podamos volver a las conexiones normales sin disturbios por parte de nuestros usuarios.

Charles
fuente
2
Espero haber leído esta respuesta antes de correrSELECT orders.* FROM orders LEFT JOIN items USING(item_id)
Ast Derek
31
Conozco un gran sitio web que ha estado utilizando conexiones persistentes durante casi una década. El truco es usar una capa sobre la extensión DB, y hacer que recuerde las cosas que deben limpiarse usando register_shutdown_function(). Si el proceso muere, la conexión también muere. Si no lo hace, la conexión se restablece a su estado limpio (por ejemplo, las transacciones abiertas se revierten). Si esto falla, la conexión se cierra y la próxima solicitud abrirá una nueva para el mismo proceso. No hay necesidad de demonizar las conexiones persistentes.
Walter Tross
Tengo curiosidad @Charles ... ¿alguna vez se resolvió su problema?
Tschallacka
@MichaelDibbets Reemplazamos el servidor de aplicaciones hace unos meses, y desconectamos pconnect para ver si el error de tres segundos todavía existía. No lo fue. Se ha resuelto por proxy, supongo. La respuesta a continuación con respecto a mysqli_change_usertodavía es probablemente la mejor solución para las personas que tienen que hacer conexiones persistentes en una aplicación no diseñada para tratar problemas de estado.
Charles
55
Tuvimos un retraso de 5 segundos en la conexión, que logramos aislar como un problema de DNS + IPv6. El servidor buscaba una dirección v6, fallaba y luego usaba la dirección IPv4.
Nigel Atkinson
45

En respuesta al problema de Charles anterior,

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

Una queja común sobre las conexiones persistentes es que su estado no se restablece antes de volver a usarlo. Por ejemplo, las transacciones abiertas y sin terminar no se revierten automáticamente. Pero también, los cambios de autorización que ocurrieron en el tiempo entre poner la conexión en el grupo y reutilizarla no se reflejan. Esto puede verse como un efecto secundario no deseado. Por el contrario, el nombre persistente puede entenderse como una promesa de que el estado persiste.

La extensión mysqli admite ambas interpretaciones de una conexión persistente: estado persistente y restablecimiento de estado antes de reutilizar. El valor predeterminado es restablecer. Antes de reutilizar una conexión persistente, la extensión mysqli llama implícitamente mysqli_change_user()para restablecer el estado. La conexión persistente le parece al usuario como si acabara de abrirse. No se ven artefactos de usos anteriores.

La mysqli_change_user()función es una operación costosa. Para un mejor rendimiento, los usuarios pueden querer recompilar la extensión con el indicador de compilación MYSQLI_NO_CHANGE_USER_ON_PCONNECTestablecido.

Se deja al usuario elegir entre un comportamiento seguro y el mejor rendimiento. Ambos son objetivos de optimización válidos. Para facilitar su uso, el comportamiento seguro se ha convertido en el predeterminado a expensas del máximo rendimiento.

Prasante
fuente
+1, si no fuera por el hecho de que hemos limpiado el desorden de otras maneras, me encantaría ver si llamar manualmente change_user hubiera solucionado nuestros extraños problemas de estado desconocido.
Charles
¿Cuál es el equivalente para las conexiones persistentes PDO Postgres? Tengo problemas similares a los que tuvo @Charles, donde después de un tiempo los usuarios recibirían un error como fetch sql: el servidor cerró la conexión inesperadamente. Esto probablemente significa que el servidor terminó de forma anormal al ejecutar una consulta SELECT simple (ni siquiera transacciones).
Carmageddon
1
@Carmageddon, eso es más adecuado para una nueva pregunta, pero el problema es que Postgres no hace pconnect y deberías usar uno de los grupos de conexiones externas.
Charles
@ Charles, ¿qué quieres decir con eso? ¿El uso de la conexión persistente del PDO no es equivalente al uso de "grupos de conexiones externas"? o a que te referias
Carmageddon
@Carmageddon, lo que quiero decir es que la comunidad de Postgres se decidió por la agrupación de conexiones como una mejor solución que pconnect. Echa un vistazo a pgbouncer o pgpool-II. No estoy seguro de que PDO conecte Postgres de todos modos, pero puedo estar totalmente fuera de control.
Charles
13

Las conexiones persistentes son una buena idea solo cuando se tarda (relativamente) mucho tiempo en conectarse a su base de datos. Hoy en día ese casi nunca es el caso. El mayor inconveniente de las conexiones persistentes es que limita la cantidad de usuarios que puede tener navegando por su sitio: si MySQL está configurado para permitir solo 10 conexiones simultáneas a la vez, cuando una undécima persona intente navegar por su sitio, no funcionará para ellos. .

PDO no gestiona la persistencia. El controlador MySQL lo hace. Reutiliza las conexiones cuando a) están disponibles y el host / usuario / contraseña / base de datos coinciden. Si hay algún cambio, no reutilizará una conexión. El mejor efecto neto es que estas conexiones que tiene se iniciarán y detendrán tan a menudo porque tiene diferentes usuarios en el sitio y hacerlos persistentes no sirve de nada.

La clave para entender sobre las conexiones persistentes es que NO debe usarlas en la mayoría de las aplicaciones web. Suenan atractivos pero son peligrosos y prácticamente inútiles.

Estoy seguro de que hay otros hilos en esto, pero una conexión persistente es peligrosa porque persiste entre las solicitudes. Si, por ejemplo, bloquea una tabla durante una solicitud y luego no se desbloquea, esa tabla permanecerá bloqueada indefinidamente. Las conexiones persistentes también son prácticamente inútiles para el 99% de sus aplicaciones porque no tiene forma de saber si se usará la misma conexión entre diferentes solicitudes. Cada hilo web tendrá su propio conjunto de conexiones persistentes y no tiene forma de controlar qué hilo manejará qué solicitudes.

La biblioteca de MySQL procesal de PHP tiene una característica por la cual las llamadas posteriores a mysql_connect devolverán el mismo enlace, en lugar de abrir una conexión diferente (como cabría esperar). Esto no tiene nada que ver con conexiones persistentes y es específico de la biblioteca mysql. PDO no exhibe tal comportamiento


Enlace de recursos: enlace

En general, podría usar esto como un "conjunto de reglas" aproximado ::

, use conexiones persistentes, si:

  • Solo hay pocas aplicaciones / usuarios que acceden a la base de datos, es decir, no dará como resultado 200 conexiones abiertas (pero probablemente inactivas), porque hay 200 usuarios diferentes compartidos en el mismo host.
  • La base de datos se ejecuta en otro servidor al que accede a través de la red

  • Una (una) aplicación accede a la base de datos muy a menudo

NO , no use conexiones persistentes, si:

  • Su aplicación solo necesita acceder a la base de datos 100 veces por hora.

  • Tienes muchos, muchos servidores web accediendo a un servidor de base de datos

El uso de conexiones persistentes es considerablemente más rápido, especialmente si está accediendo a la base de datos a través de una red. No hace mucha diferencia si la base de datos se ejecuta en la misma máquina, pero sigue siendo un poco más rápida. Sin embargo, como su nombre lo indica, la conexión es persistente, es decir, permanece abierta, incluso si no se usa.

El problema con eso es que, en la "configuración predeterminada", MySQL solo permite 1000 "canales abiertos" paralelos. Después de eso, se rechazan las nuevas conexiones (puede modificar esta configuración). Entonces, si tiene, digamos, 20 servidores web con cada 100 clientes en ellos, y cada uno de ellos tiene solo un acceso a la página por hora, las matemáticas simples le mostrarán que necesitará 2000 conexiones paralelas a la base de datos. Eso no funcionará.

Ergo: Úselo solo para aplicaciones con muchas solicitudes.

Jhonathan H.
fuente
44
Después de la línea, su respuesta es copiar y pegar desde stackoverflow.com/a/51583/718224
Tony Stark
1
"SÍ, use conexiones persistentes, si: [...] Hay pocas aplicaciones / usuarios que acceden a la base de datos" contradice con "Úselo solo para aplicaciones con muchas solicitudes". Sin embargo, esto último es correcto. Situación: miles de solicitudes por segundo darán como resultado cientos de conexiones de bases de datos activas. Cuando un sistema se escala linealmente, también escalará linealmente la cantidad de conexiones a la base de datos. Por lo tanto, más solicitudes (más usuarios) generarán más conexiones. Por lo tanto, necesita conexiones limitadas (!) Pero muchas activas cuando tiene muchas solicitudes (usuarios)
Ken Van Hoeylandt
12

En mis pruebas, tuve un tiempo de conexión de más de un segundo con mi host local, por lo tanto, suponiendo que debería usar una conexión persistente. Otras pruebas mostraron que era un problema con 'localhost':

Resultados de la prueba en segundos (medidos por microtiempo php):

  • web alojada: connectDB: 0.0038912296295166
  • localhost: connectDB: 1.0214691162109 (más de un segundo: ¡no use localhost!)
  • 127.0.0.1: connectDB: 0.00097203254699707

Curiosamente: el siguiente código es tan rápido como usar 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));
Gunnar Bernstein
fuente
¡Parece que PDO tiene dificultades para traducir nombres de dominio! Gracias, me preguntaba por qué cada conexión tardaba mucho en mi máquina de cuatro núcleos.
Mustafa
@Gunnar Bernstein +1 buen hallazgo. "localhost" ciertamente toma más tiempo y esto ha mejorado un poco la velocidad de mi aplicación web (hace muchas conexiones).
imperium2335
1
Esto es genial. Algo está mal con la resolución en mi máquina de desarrollo ... el uso de una IP llevó mi script de 6.1s a 1.1s
Pete
localhostusa conexión de socket, la conexión de socket es famosa por ser mala en una gran cantidad de conexiones
mente
@mente ¿Alguna referencia, recurso que pueda probar ese hecho? Tendería a pensar que los UDS se prefieren sobre TCP. Gracias.
Nuxwin
6

Las conexiones persistentes deberían aumentar considerablemente el rendimiento. No estoy de acuerdo con la afirmación de que debe "evitar" la persistencia.

Parece que las quejas anteriores son impulsadas por alguien que usa tablas MyIASM y piratea sus propias versiones de transacciones agarrando los bloqueos de la mesa ... ¡Bueno, por supuesto que vas a llegar a un punto muerto! Use beginTransaction () de PDO y mueva sus tablas a InnoDB ..

Stephen
fuente
2
Un año después, me doy cuenta, pero para que conste: mi historia proviene de una base de datos compuesta completamente de tablas InnoDB, con la única excepción de un puñado de clones desnormalizados atrapados en el atolladero de MyISAM para soporte de indexación de texto completo.
Charles
Pfft, Sphinx está viejo y roto, ElasticSearch es la nueva moda. Un buen día, en realidad lo usaremos para nuestras viejas aplicaciones en lugar de solo las nuevas ...
Charles
La búsqueda de texto completo en PostgreSQL es el verdadero ganador. Es asombroso. No requiere otra herramienta / servidor en ejecución para hacer su trabajo. No tiene que preocuparse por mantener los datos sincronizados. Controles muy granulares. Múltiples diccionarios o escribe el tuyo. Y dado que PostgreSQL usa automáticamente consultas de índice múltiple, puede colocarlo con cualquier otra consulta que esté ejecutando.
brightball
2
MySQL 5.6 ofrece soporte de texto completo para tablas InnoDB.
timetofly
2

Me parece que tener una conexión persistente consumiría más recursos del sistema. Tal vez una cantidad trivial, pero aún así ...

Lápiz Violento
fuente
A menudo, un intercambio de mucho tiempo humano por microsegundos de tiempo de computadora
Andy Chase
1

La explicación para usar conexiones persistentes es obviamente reducir la cantidad de conexiones que son bastante costosas, a pesar del hecho de que son considerablemente más rápidas con MySQL en comparación con otras bases de datos.

El primer problema con conexiones persistentes ...

Si está creando miles de conexiones por segundo, normalmente no se asegura de que permanezca abierto durante mucho tiempo, pero el Sistema Operativo sí. Basado en el protocolo TCP / IP Los puertos no pueden reciclarse instantáneamente y también tienen que invertir un tiempo en la etapa "FIN" esperando antes de que puedan reciclarse.

El segundo problema ... usar muchas conexiones de servidor MySQL.

Muchas personas simplemente no se dan cuenta de que es capaz de aumentar la variable * max_ connections * y obtener más de 100 conexiones simultáneas con MySQL, otras fueron superadas por problemas anteriores de Linux de la incapacidad de transmitir más de 1024 conexiones con MySQL.

Permite hablar ahora sobre por qué las conexiones persistentes se deshabilitaron en la extensión mysqli. A pesar del hecho de que puede usar mal las conexiones persistentes y obtener un rendimiento deficiente, esa no fue la razón principal. La razón real es que puede tener muchos más problemas con él.

Las conexiones persistentes se pusieron en PHP en ocasiones de MySQL 3.22 / 3.23 cuando MySQL no era tan difícil, lo que significa que puede reciclar conexiones fácilmente sin problemas. En versiones posteriores, sin embargo, surgieron muchos problemas: si recicla la conexión que tiene transacciones no confirmadas, se mete en problemas. Si recicla conexiones con configuraciones de juegos de caracteres personalizados, corre peligro nuevamente, así como también sobre las variables posiblemente transformadas por sesión.

Un problema con el uso de conexiones persistentes es que realmente no escala tan bien. Para aquellos que tienen 5000 personas conectadas, necesitará 5000 conexiones persistentes. Para evitar el requisito de persistencia, es posible que tenga la capacidad de atender a 10000 personas con una cantidad similar de conexiones porque están en condiciones de compartir conexiones individuales cuando no están con ellas.

Tony Stark
fuente
0

Me preguntaba si una solución parcial sería tener un grupo de conexiones de uso único. Podría dedicar tiempo a crear un grupo de conexiones cuando el sistema tenga poco uso, hasta un límite, distribuirlos y eliminarlos cuando se hayan completado o se haya agotado el tiempo de espera. En segundo plano, está creando nuevas conexiones a medida que se toman. En el peor de los casos, esto debería ser tan lento como crear la conexión sin el grupo, suponiendo que establecer el enlace es el factor limitante.

James
fuente