[caracas-pm] Re: [l-linux] perl+postgres y el consumo de memoria

Ernesto Hernández-Novich emhn at telcel.net.ve
Thu Mar 3 10:47:38 PST 2005


On Thu, 2005-03-03 at 09:00 -0800, Daniel Cubero Salas wrote:
> Desarrolle un programa en perl con conexin a postgres
> (digamos que es un semi-daemon), cuando se inserta en
> cierta tabla: este programa, cada 3 segundos, lee todo
> lo que este en la tabla a travs de una funcin que
> cree en postgres y hace ciertas operaciones. Esto
> funciona muy bien. 
>
> El problema es: observando el consumo de memoria noto
> que  no libera memoria, con MRTG (q reporta el consumo
> de memoria)

MRTG no reporta el consumo de memoria. MRTG utiliza el protocolo SNMP (o
un programa auxiliar) para tomar muestra, almacenarlas en una base de
datos round-robin y construir gráficos de tendencia; tu lo estarás
usando para tomar muestras de uso de memoria. Esto para los que leen por
primera vez acerca de MRTG antes que se confundan.

> se genera una curva descendente que llega
> a estabilizarse en 20 MB de memoria libre (hay
> ocaciones en que usa algo de swap) y queda all
> constante.

Por tu descripción eso lo que reporta es la memoria libre en _todo_ el
sistema. Quizás se está consumiendo en otras cosas, y tu asumes que es
el programa en Perl. Es mucho mejor que controles la memoria consumida
por el proceso en cuestión, cosa que puedes hacer con MRTG apoyado en un
programa externo que haga ps y saque algunas cuentas triviales.

Pero asumamos que la memoria libre se agota porque éste programa la
consume toda.

> Me parece que es algo en como estoy manejando la
> conexin al postgres y el almacenamiento de la matriz
> de datos que saco de la tabla... a breves rasgos hace
> esto el programita:
> 
> ============
> $dbh = BI->connect_cached(<Conecta correctamente>)
> 
> $sth  = $dbh->prepare_cached(
>         "SELECT * FROM funcion_consulta(?)");

Usar variables globales sin declararlas es el primer peor error que se
puede cometer. Si tienen que ser globales, entonces

use vars qw/$dbh $sth/

pero si no

my $dbh = ...
my $sth = ...

Es más, acostúmbrate a poner 'use strict' al comienzo de todos los
programas para que el lenguaje te advierta de esas (y otras) cosas
perniciosas.

> for (;;) {
>   if ( $sth->execute($dato) ) {
>     while ( @rowData = $sth->fetchrow() )

Resulta que @rowData es global... simplemente porque no la declaraste
como local (cosa que sería interesante para que se _libere_ cada vez que
cumplas un ciclo, cosa que probablemente es lo que _quieres_ pero no lo
que _escribiste_.

Por otro lado, hacer fetchrow() obliga a crear una _lista_ con los
valores de las columnas, para que luego sea _copiada_ a @rowData. Eso no
es eficiente en velocidad, sobre todo en un ciclo como el que estás
escribiendo y peor aún si esperas traer muchos registros.

La primera solución, que no implica cambiar mucho tu algoritmo, es que
en lugar de hacer fetchrow() hagas fetchrow_arrayref() para que te
devuelva una referencia a la lista, así cambias a

while( my $rowData = $sth->fetchrow_arrayref() ) {
  ...
  usas $rowData->[0] en lugar de $rowData[0] como tendrías antes
  ...
}

te estás ahorrando copiar una lista (eso es menos tiempo, menos memoria
y menos necesidad de que la máquina virtual recoja basura).

Si quieres que el programa sea _brutalmente_ rápido y consuma mucha
menos memoria, entonces haces algo como

my ($columna0,$columna1,...,$columnaN);
$sth->bind_columns(\$columna1,\$columna1,...,\$columnaN);
for(;;) {
  while ($sth->fetch()) {
  ... usas $columna0, $columna1, etc. ... mágico
  }
}

Esta es la forma más rápida y menos onerosa de hacer consultas.

>     {
>          <HACE ALGUNAS OPERACIONES>
>     }
> 
>   } # FIN del IF
> } # FIN del FOR
> 
> $sth->finish();

El finish() debe utilizarse después de que "dejes de necesitar" el
execute(). De modo que debería estar dentro del if, justo después del
while. Esto es particularmente importante si la consulta que estás
haciendo utiliza ORDER BY o GROUP_BY; como consultas contra una tabla
generada por función (Hola usuarios MySQL que no pueden hacer eso :-)
sólo tu sabrás si te resulta útil.

> $dbh->disconnect();
> exit (0);
> 
> ============
> 
> Preguntas: 
> Hay alguna diferencia importante entre las conexiones
> que son y no son "CACHED"???

Si.

> Debera colocar el "$sth-PREPARE" dentro del FOR

No, ahí está perfectamente bien. Las instrucciones se preparan una sola
vez, se ejecutan múltiples veces.

> y hacer $sth->finish() dentro del FOR, tambin;

Si. Como expliqué antes.
-- 
Ernesto Hernández-Novich - On Linux 2.6.11 i686 - Unix: Live free or die!
Geek by nature, Linux by choice, Debian of course.
If you can't apt-get it, it isn't useful or doesn't exist.
GPG Key Fingerprint = 438C 49A2 A8C7 E7D7 1500 C507 96D6 A3D6 2F4C 85E3



More information about the caracas-pm mailing list