Escribir un archivo CSV muy grande desde la salida de la base de datos en PHP

Tengo una base de datos de datos de sensores que se recopilan cada segundo. Al cliente le gustaría poder descargar fragmentos de 12 horas en formato CSV. Todo está hecho.

Lamentablemente, la salida no es datos directos y debe procesarse antes de que se pueda crear el CSV (las partes se almacenan como JSON en la base de datos), por lo que no puedo simplemente volcar la tabla.

Entonces, para reducir la carga, pensé que la primera vez que se descargaba el archivo, lo almacenaría en caché en el disco, luego, si hubiera más solicitudes, solo descargaría ese archivo.

Si no trato de escribirlo (usando file_put_contents, FILE_APPEND), y simplemente repito cada línea, está bien, pero al escribirlo, incluso si le doy al script 512M, se queda sin memoria.

entonces esto funciona

while($stmt->fetch()){
    //processing code
    $content = //CSV formatting
    echo $content;
}

Esto no lo hace

while($stmt->fetch()){
    //processing code
    $content = //CSV formatting
    file_put_contents($pathToFile, $content, FILE_APPEND);
}

Parece que incluso si estoy llamando a file_put_contents en cada línea, lo está almacenando todo en la memoria.

¿Alguna sugerencia?

preguntado el 16 de mayo de 11 a las 17:05

¿Ha intentado usar fwrite () para escribirlo línea por línea en su lugar? -

2 Respuestas

El problema es que file_put_contents está intentando volcar todo de una vez. En su lugar, debe recorrer el formato y usar fopen, fwrite, fclose.

while($stmt->fetch()){
    //processing code
    $content[] = //CSV formatting
    $file = fopen($pathToFile, a);
    foreach($content as $line)
    {
        fwrite($file, $line);
    }
    fclose($file);
}

Esto limitará la cantidad de datos que se intentan intercambiar en los datos en un momento dado.

contestado el 16 de mayo de 11 a las 21:05

Si bien es esencialmente correcto, observe cómo en los usos originales echo $content entonces es solo $content = // CSV formatting ; fwrite($file, $content); y mueve el fopen y fclose fuera del bucle principal. - chx

¿Está seguro de que es realmente necesario abrir y cerrar el archivo todo el tiempo? De lo contrario, estoy de acuerdo, intente escribir en el archivo en línea a la vez usando fwrite. - Adrian Schmidt

¿Alguien quiere condiciones de carrera? ¿Qué pasa si un segundo usuario carga esto mientras otro todavía está esperando que se cargue? Me imagino que la corrupción de datos sería posible, e incluso probable, ya que en un entorno de oficina los usuarios podrían ir a ver tal cosa al mismo tiempo. - sakatc

@sakatc Entonces guardaría el archivo en caché, así que si hay un archivo que no sea más antiguo que X, sirva ese archivo. - MPV

Estoy completamente de acuerdo con escribir una línea a la vez, nunca tendrá problemas de memoria de esta manera, ya que nunca hay más de una línea cargada en la memoria a la vez. Tengo una aplicación que hace lo mismo. Sin embargo, un problema que he encontrado con este método es que el archivo tarda una eternidad en terminar de escribir. Entonces, esta publicación es para respaldar lo que ya se ha dicho, pero también para pedirles a todos una opinión sobre cómo acelerar esto. Por ejemplo, mi sistema limpia un archivo de datos contra un archivo de supresión, así que leo una línea a la vez y busco una coincidencia en el archivo de supresión, luego, si no se encuentra ninguna coincidencia, escribo la línea en el nuevo archivo limpiado . Sin embargo, un archivo de 1k líneas tarda unas 50 horas en completarse, así que espero encontrar una forma mejor. He intentado esto de varias maneras, y en este punto cargo todo el archivo de supresión en la memoria ahora para evitar que mi ciclo de lectura principal tenga que ejecutar otro ciclo a través de cada línea en el archivo de supresión, pero incluso eso todavía está tomando horas.

Entonces, línea por línea es, con mucho, la mejor manera de administrar la memoria de su sistema, pero me gustaría obtener el tiempo de procesamiento para un archivo de 50k líneas (las líneas son direcciones de correo electrónico y nombres y apellidos) para terminar de ejecutarse en menos de 30 minutos si es posible.

fyi: el archivo de supresión tiene un tamaño de 16,000 kb y la memoria total utilizada por el script según lo indicado por memory_get_usage () es de aproximadamente 35 megas.

¡Gracias!

Respondido 04 ago 11, 10:08

No es la respuesta que estás buscando? Examinar otras preguntas etiquetadas or haz tu propia pregunta.