¿Te ha pasado que al correr un script Php te agotas la memoria y te bota un “Allowed Memory Size Exhausted”?
Si es así, hay de dos sopas:
Aumentar la memoria permitida. Esta es la opción rápida y sucia, no es eficiente, no muy recomendable, o mejor dicho, para nada en absoluto.
Eficientizar el uso de memoria de tu script. Esta es la opción eficiente, elegante, la manera a seguir.
Ahora, ¿Cómo eficientizas el uso de memoria en Php? Pues liberándola. Aunque Php cuenta con un garbage collector, es buena práctica no delegarle todo el trabajo, lo que ya no estes ocupando, libéralo, en especial esos arreglos de objetos con muchas propiedades. De hecho, este es el ejemplo perfecto.
Supongámos que estas obteniendo información de una base de datos y vacíandola en archivvos para su rápido acceso fuera de línea o por un servidor FTP, y estas creando una estructura de archivos de este tipo:
Cliente (carpeta)
Año (carpeta)
Mes (este es el archivo, csv digámos)
Pues bien, necesitarás consultas que te traigan los clientes, luego para cada cliente los años que tiene en… digamos ventas, luego para cada cliente y cada año ahora si sus ventas. Cada consulta la almacenas temporalmente en un arreglo.
El algoritmo sería como sigue:
Primero obtienes la totalidad de clientes y lo almacenas en un arreglo.
Recorres ese arreglo y para cada cliente obtienes los años en los que tiene ventas y los almacenas en otro arreglo.
Recorres ese otro arreglo y para cada cliente y año, buscas sus ventas, las almacenas en otro arreglo, exportar ese arreglo a formato CSV y guardas el archivo.
Pues bién, cada arreglo que vas almacenando va consumiendo memoria, bastante, aunque lo vayas reemplazando en cada paso, la memoria se mantiene, ya que lo tienes almacenado mientras realizas la siguiente consulta, obtienes los datos de la consulta en memoria al momento del reemplazo, es decir que estás duplicando información en cada iteración, lo cual lo vuelve exponencial.
Para evitar esto, simplemente libera cada variable volviendola nula en cuanto la dejes de necesitar. El algoritmo en pseudo-código quedaría como esto:
[php]
get(“clientes”);
// Recorremos los clientes
foreach ($clientes as $cliente) {
// Creamos la carpeta
mkdir($cliente[“cliente_id”]);
// Obtenemos los anios
$sql = “SELECT DISTINCT DATEPART(YEAR, fecha) AS anio FROM ventas WHERE cliente_id = :cliente_id”;
$anios = $db->query($sql, array(“cliente_id” => $cliente[“cliente_id”]));
// Recorremos los anios
foreach( $anios as $anio) {
// Creamos la carpeta
mkdir($cliente[“cliente_id”].”/”.$anio);
// Obtenemos los meses
$sql = “SELECT DISTINCT DATEPART(MONTH, fecha) AS mes FROM ventas WHERE cliente_id = :cliente_id AND DATEPART(YEAR, fecha) = :anio”;
$meses = $db->query(
$sql,
array(
“cliente_id” => $cliente[“cliente_id”],
“anio” => $anios[“anio”]
)
); // end db query
// Recorremos los meses
foreach ($meses as $mes) {
// Obtenemos las ventas
$sql = “SELECT * FROM ventas WHERE cliente_id = :cliente_id AND DATEPART(YEAR, fecha) = :anio AND DATEPART(MONTH, fecha) = : mes”;
$ventas = $db->query(
$sql,
array(
“cliente_id” => $cliente[“cliente_id”],
“anio” => $anios[“anio”],
“mes” => $meses[“mes”]
)
); // end db query
// Exportamos las ventas
$csv = to_csv($ventas);
// Ya no necesitamos $ventas, lo liberamos
$ventas = null;
// Guardamos el resultado
file_put_contents($cliente[“cliente_id”].”/”.$anio.”/”.$mes[“mes”].”.csv”, $csv);
// Ya no necesitamos $csv, lo liberamos
$csv = null;
} // end foreach mes
// Ya no necesitamos $meses, lo liberamos.
$meses = null;
} // end foreach anios
// Ya no necesitamos $anios, lo liberamos
$anios = null;
} // end foreach clientes
// Ya no necesitamos $clientes, lo liberamos
$clientes = null;
[/php]
Ahora nuestro script podrá manejar mejor la memoria y no alcanzaremos el límite, terminando de pronto la tarea con un error.
Hay otras maneras de eficientizar la memoria,, como mandar llamar directamente las funciones con los resultados de otra función, de esta manera nos ahorramos el almacenamiento en variables y no tenemos que liberarlas, así:
[php]
file_put_contents(
$cliente[“cliente_id”].”/”.$anio.”/”.$mes[“mes”].”.csv”,
to_csv(
$db->query(
$sql,
array(
“cliente_id” => $cliente[“cliente_id”],
“anio” => $anios[“anio”],
“mes” => $meses[“mes”]
) // end parameters array
) // end db query
) // end to_csv
); // end file_put_contents
[/php]
Aunque de esta manera, para mí, el código se vuelve menos legible. Está en tus manos, es la forma en que más te acomodes para programar. Gracias por leerme y a seguir tirando código :).
admin
February 12, 2019
PHP
No Comment