PHP, YII2 y la formación de grandes archivos de Excel

Inicio


Un sistema de contabilidad e informes respaldado por nuestra empresa comenzó a crecer muy rápidamente en la cantidad de datos almacenados. El sistema está escrito en PHP usando el marco Yii2. Inicialmente, los informes se crearon a través de la biblioteca PhpSpreadsheet, que reemplazó al obsoleto obsoleto, PhpExcel.

Entre los diferentes tipos de informes, había uno muy grande; de ​​hecho, el conjunto completo de todos los datos almacenados en la base de datos debería cargarse en una tabla de Excel. En la etapa inicial, no hubo problemas, pero cuando el volumen comenzó a exceder muchos cientos de miles de registros, el script de formación de descarga comenzó a caerse en el límite de tiempo de espera. Para empezar, aumentamos este límite y comenzamos a buscar formas de resolver el problema. Pero una solución temporal no duró mucho: el problema con el límite de tiempo se convirtió en un problema con el límite de memoria. Lanzaron la "RAM" al servidor y eliminaron el límite de memoria para esta operación en particular. Muy pronto, los usuarios comenzaron a quejarse de los errores de tiempo de ejecución nuevamente. Tuve que eliminar el límite de tiempo para el informe completo. Pero sentarse y mirar una docena de minutos en la pantalla con un indicador de carga no es muy divertido. Además, a veces se necesitaba un informe "aquí y ahora", y cada minuto dedicado a su formación resultó ser crítico. Los experimentos con la configuración del entorno se detuvieron, se rascaron la parte posterior de la cabeza y comenzaron a optimizar el código.

Busca una solución


Lo primero que se hizo fue colocar el script de informes en el proceso en segundo plano y el usuario supervisa el progreso a través de la "barra de progreso". La ejecución del trabajo en segundo plano se implementó a través del mecanismo de cola usando Redis para el almacenamiento. El trabajo en el sistema no se detiene, puede realizar otras tareas y regresar periódicamente a la página del informe para ver si el archivo está listo. Tan pronto como se forma el archivo, se le ofrece al usuario un enlace de descarga. Pero, como se mencionó anteriormente, a veces el archivo fue requerido "inmediatamente", y el aumento de la usabilidad no resolvió este problema. Mientras tanto, la cantidad de datos continuó creciendo y el tiempo que tomó construir el archivo llegó a 79 minutos. Esto es completamente inaceptable, especialmente teniendo en cuenta que los informes son uno de los fundamentos de la funcionalidad de este sistema. No, todas las otras partes funcionaron como un reloj, pero esta mosca en la pomada echó a perder la impresión general.

Primeros resultados


Nos sentamos nuevamente para el análisis de código. Lo primero que se probó fue el proceso de selección de datos de la base de datos. Pero las consultas ya se han optimizado de la mejor manera posible. Aunque la solicitud más larga fue una muestra terrible con cinco o seis llamadas a la monstruosa FIAS, funcionó en 2-5 segundos. El punto débil no era él, sino la formación del archivo "exelnik". Los intentos han comenzado a optimizar este proceso. Comenzando desde el almacenamiento en caché en redis, hasta perversiones, como la formación de pequeños "sobresalientes" separados en flujos paralelos, seguidos de pegar en un archivo. Pero el resultado siempre fue el mismo: el problema con el tiempo se convirtió en un problema con la memoria y viceversa. No había término medio, solo fluía de un extremo a otro. Después de una cierta cantidad de datos, el consumo de recursos de la biblioteca comenzó a crecer exponencialmente y no fue posible derrotarlo. PhpSpreadsheet: no es adecuado para archivos grandes. Como resultado, se decidió cambiar la biblioteca. Como opción, escribir su propio análogo para la formación de ex archivos.

Análisis y selección de herramientas.


No se apresuraron a escribir bicicletas, pero para empezar, analizaron las soluciones existentes. De las posibles opciones, solo la caja / boquilla era de interés. Reescribió rápidamente el módulo utilizando esta biblioteca. Como resultado, se obtuvo un informe completo en 145 segundos. Permítame recordarle que las últimas pruebas con PhpSpreadsheet son 79 minutos, ¡y aquí 2.5 minutos! Pruebas realizadas: aumentó la cantidad de datos en 2 veces. El informe se generó en 172 segundos. La diferencia es asombrosa. Por supuesto, la biblioteca no tiene las mismas funciones que PhpSpreadsheet, pero en este caso el conjunto mínimo de herramientas es suficiente, ya que la velocidad es crítica.

Extensión para Yii2


La decisión final fue enmarcada como una extensión para Yii2. Tal vez alguien sea útil. La extensión le permite cargar cualquier conjunto de datos desde GridView para sobresalir mientras mantiene el filtrado y la clasificación. Utiliza yii / queue y box / spout como dependencias. Tiene sentido usar la extensión para formar archivos realmente grandes, bueno, al menos 50,000 líneas =) Por el momento, el módulo, que se ha convertido en la base de la extensión, hace frente a una carga de casi 600,000 líneas.

Enlace a github: extensión Yii2 ExcelReport

Gracias por su atencion!

Source: https://habr.com/ru/post/es420393/


All Articles