Pool de conexiones en PHP

Rodrigo Fuentealba darkprox en gmail.com
Vie Feb 10 15:08:59 CLST 2006


JuanPa wrote:
> Hola listeros....
> Te agradezco nuevamente Rodrigo por dar tu valioso tiempo aclarando 
> mis dudas,
>
> A raiz de es comentario es que he optado por utilizar mis clasicas 
> conexiones no persistentes .
ejalé ;)
>
>     Es mejor usar conexiones no persistentes, es decir, abriendo la
>     conexión
>     al principio de tu script y cerrando la conexión al final, pues
>     siempre
>     vas a tener páginas que no hagan ninguna consulta a la base de
>     datos...
>     ¿Para qué, entonces, tendrás una conexión abierta? Es únicamente un
>     desperdicio de recursos.
>
>
> Ademas no voy a esperar que el GC (Garbage Colector) haga su trabajo, 
> una ultima cosa...
Bueno el recolector de basura funcionará igual, pero cerrando menos cosas.
>
>     Por eso es mejor abrir la conexión al principio de un script y
>     cerrarla
>     al final, y si tienes funciones que recuperan datos como el caso
>     de tu
>     buscar alumno, en vez de abrir una conexión nueva, preguntes si ya hay
>     una conexión abierta para aprovecharla.
>
>
> Pero ¿ esto no significa que la conexion deberìa ser persistente? o te 
> refieres a que deberia pasar por parametros a mi objeto conexion que 
> tiene un recurso de conexion interno($link) y luego verificar si esta 
> está abierta.
Una conexión no persistente se abre al inicio de un archivo (supongamos 
insertar.php) y se debe cerrar al final de éste. Si tienes un archivo 
que se llama modificar.php, ese debe declarar otra conexión, pues la 
primera no estará.

La conexión persistente se abre al inicio de la sesión (mientras el 
usuario está conectado) y puede pasarse por varias páginas. Si tienes un 
archivo insertar, modificar, eliminar, buscar, etc, cuantoquieras.php, 
todos esos archivos usarán conexiones persistentes.

<!-- leo de otro mail
una conexion persistente permite re-utilizar una conexion ya abierta, 
ademas de tener que cerrarla explicitamente sino queda "abierta"! la 
idea es evitar el tiempo (milisegundos?) que se ocupa en "establecer" 
una conexion. su uso a mi entender esta reservado a sitios con muchas 
conexiones nuevas, con procesos cortos.
-- leido -->

Esto es cierto, yo dije "una vez por usuario/sesión", queriendo enfocar 
a que la conexión estaba abierta y podía ser aprovechada por varias 
páginas que visita un usuario desde un navegador. (pues una conexión se 
abre solamente para un usuario).

> Y finalmente cuando dices...
>
>     Cualquiera de las dos conexiones funciona así, la diferencia es
>     que la
>     persistente la abres una vez por usuario (o por conexion?) ...
>
> Esto significa ¿ Que la conexion seguira abierta en cada script que 
> valla ejecutando el usuario, mientras no se cierre explicitamente en 
> otro script o cuando el usuario cierre el navegador ?
Ahhh, eso con ejemplo (ahora haré código real con adodb)... "notese, 
solamente una conexión no persistente"

<?php
    echo("Abriendo la primera conexión")
    $conexion = ADONewConnection("postgres");
    $conexion->NConnect($host,$user,$pass,$base);
    $sql = $conexion->Prepare("SELECT * FROM alumno WHERE rut_alumno = ?");
    $rs = $conexion->Execute($sql,array('12345678-9'));
    $conexion->Close();

    echo("Abriendo la segunda conexión");
    $conexion = ADONewConnection("postgres");
    $conexion->NConnect($host,$user,$pass,$base);
    $sql = $conexion->Prepare("SELECT * FROM apoderado WHERE rut_alumno 
= ?");
    $rs = $conexion->Execute($sql,array('12345678-9'));
    $conexion->Close();

    $conexion = ADONewConnection("postgres");
    $conexion->NConnect($host,$user,$pass,$base);
    $sql = $conexion->Prepare("SELECT * FROM cuotas WHERE rut_alumno = ?");
    $rs = $conexion->Execute($sql,array('12345678-9'));
    $conexion->Close();
?>

El ejemplo anterior es un script demasiado mega mal hecho. Abres tres 
veces una conexión nueva y no destruyes las conexiones anteriores. 
Digamos que solamente las desconectas. Uhhh, en este pequeño script te 
gastaste tres conexiones. ¿Qué mal, no?. Pues ahora veamos cómo lo 
habría hecho con una sola conexión no persistente.

<?php
    echo("Abriendo la conexión")
    $conexion = ADONewConnection("postgres");
    $conexion->NConnect($host,$user,$pass,$base);

    dibujemos();

    // con la misma conexion no persistente, puedes hacer UNA
    $sql = $conexion->Prepare("SELECT * FROM alumno WHERE rut_alumno = ?");
    $rs = $conexion->Execute($sql,array('12345678-9'));

    juguemos_en_el_bosque();

    // DOS
    $sql = $conexion->Prepare("SELECT * FROM apoderado WHERE rut_alumno 
= ?");
    $rs = $conexion->Execute($sql,array('12345678-9'));

    mientras_el_lobo_no_esta();

    // TRES y más consultas en el mismo script.
    $sql = $conexion->Prepare("SELECT * FROM cuotas WHERE rut_alumno = ?");
    $rs = $conexion->Execute($sql,array('12345678-9'));

    lobo_esta(?);

    // Y antes de cerrar el script, la conexión se cierra sola.
    $conexion->Close();

    terminado();

    exit();
?>

El ejemplito este aprovecha una sola conexión no persistente, pero 
usando solamente "UN" script, lo cual a pesar de que está bien, te 
ensucia un poco el código y te lo hace un poquito ilegible, por todas 
esas SQL por aquí y por allá. OK, ahora vamos a hacer un script para las 
funciones, un script para abrir y cerrar la conexión, y tu script de 
lógica de negocios:

<!-- conexion.php -->
<?php
    // nuestra función que nos abre muchas puertas
    function abrir_conexion()
    {
        require_once('adodb/adodb.inc.php');

        $host = "123.123.123.123";
        $user = "postgres";
        $pass = "nula";
        $base = "carepescao";

        $conexion = ADONewConnection("postgres");
        $conexion->NConnect($host, $user, $pass, $base);

        if(!$conexion->IsConnected)
        {
           die("Arghhh mi jefe me va a matar! me pitié la conexión");
        }
    }

    // la función que cierra con llave cuando nos vayamos ;)
    function cerrar_conexion();
    {
        if($conexion->IsConnected)
        {
            $conexion->Close();
        }      
    }

?>

<!-- leo nuevamente:
originalmente tu preguntaste por un "pool" de conexiones lo cual es 
diferente ya que ahi se puede compartir conexiones *entre* procesos, y 
eso en PHP se tiene que hacer con ayuda externa.
-->

Nótese que compartir conexiones entre procesos (pooling) tiene 
diferencias con aprovechar las conexiones no persistentes: lo que vamos 
a hacer en el siguiente archivo aprovecha una conexión no persistente 
que esté abierta, en vez de estar abriendo y cerrando. Esto para mí 
también es llamado pooling, pero básico, y aunque no sea un proceso 
automatizado, así es como trabaja... La idea es siempre tener una y sólo 
una conexión disponible cuando la usemos sin tener que abrirla más que 
lo necesario, y cerrarla cuando ya tengamos todas nuestras operaciones 
hechas y no vayamos a usarla de inmediato, bien por estar requiriendo 
datos, bien por ya haberlos obtenido y estamos mostrándolos en otra 
página. La cerramos para liberar recursos y así optimizamos cualquier 
cantidad de memoria. Esta clase de pooling no comparte conexiones entre 
procesos (para mí un proceso tiene un PID y tiene un destino distinto).

<!-- sql.php -->
<?php
    require_once 'conexion.php';

    // una funcion que lo unico que hace es retornar el recordset.
    function buscar_alumno($rut)
    {
        if(!$conexion->IsConnected || !isset($conexion))
        {
           abrir_conexion();
       }
       $sql = $conexion->Prepare("SELECT * FROM alumno WHERE rut_alumno 
= ?");
       $rs = $conexion->Execute($sql,array($rut));
       return $rs;
    }

    // una funcion que lo unico que hace es retornar el recordset.
    function buscar_apoderado($rut)
    {
        if(!$conexion->IsConnected || !isset($conexion))
        {
           abrir_conexion();
       }
       $sql = $conexion->Prepare("SELECT * FROM apoderado WHERE 
rut_alumno = ?");
       $rs = $conexion->Execute($sql,array($rut));
       return $rs;
    }

    // una funcion que lo unico que hace es retornar el recordset.
    function buscar_cuotas($rut)
    {
        si la conexión no existe, o no se ha seteado, entonces abrimos 
la conexion. Si la conexion existe, para que?
        if(!$conexion->IsConnected || !isset($conexion))
        {
           abrir_conexion();
        }
        $sql = $conexion->Prepare("SELECT * FROM cuotas WHERE rut_alumno 
= ?");
        $rs = $conexion->Execute($sql,array($rut));
        return $rs;
    }
?>

<!-- bussiness.php -->
<?php
    require_once 'conexion.php';
    require_once 'sql.php';

    // abrimos la conexion();
    abrir_conexion();

    // procesamos algunas cosas...
    dibujemos();

    // llamamos a nuestra consulta.
    // esto ya es lógica de negocios, ¿no?  
    $resultado1 = buscar_alumno($rut);

    // tururururu, me gusta cantar...
    juguemos_en_el_bosque();

    // nuestra logica de negocios requiere que
    // busquemos al responsable por el cabro de ... ;)
    $resultado2 = buscar_apoderado($rut);

    // el lobo andaba comiéndose unas abuelitas por ahi
    mientras_el_lobo_no_esta();

    // de nuevo, no necesitamos de SQL feos por ahi...
    $resultado3 = buscar_cuotas($rut);

    // mostramos los resultados en la pantalla...
    lobo_esta(?);

    // cerramos la conexion...
    cerrar_conexion();

    // cerramos algunas otras cosillas, o bien cargamos el footer, que 
se yo,
    terminado();

    // una vieja costumbre mía, heredada de los antiguos BASIC de ATARI.
    // nadie me ha convencido de que esto no es necesario
    exit();
?>

Como puedes ver, finalmente son más archivos, pero el que te interesa 
que es la lógica de negocios, te queda lindo para leer. La cosa es 
diseñar un poco más ordenado. El que programes en 3, 4, 5 o 100 capas si 
quieres no significa que las capas no se interconecten, pues la lógica 
de presentación necesita que la lógica de negocios le pase datos, y la 
lógica de negocios necesita conectarse a la base de datos, para que 
tenga algo que procesar!!!
> Mil disculpas si soy muy pregunton, pero siempre me gusta sacar 
> conclusiones valiosas de las conversaciones.... Gracias de antemano...
Cero dramas. Si Google no es claro en el asunto (de hecho no lo fue 
cuando lo investigué) no hay problemas en discutirlo y aclarar las dudas.
> Nos leemos
> -----------------
> Juan Pablo
------------ próxima parte ------------
Se ha borrado un adjunto en formato HTML...
URL: http://listas.inf.utfsm.cl/pipermail/php/attachments/20060210/d1b99158/attachment-0001.html


Más información sobre la lista de distribución PHP