PHP 大量数据时脚本内部服务器错误

PHP Script Internal Server Error when lots of data

总结

这是一个脚本(CakePHP 2.10.18 - LAMP 专用服务器 PHP 5.3)从 2 MySQL tables,然后对数据做一些处理输出到excel.

Table 1 有 用户 ,Table 2 有关于这些用户的 信息每个用户一条记录)。该脚本的目标是从 Table 1 中抓取用户的记录,从 Table 2 中抓取其相关信息,并将其放在 excel 行中(使用 PHPExcel_IOFactory 图书馆)。

从那些table中提取的信息是每个大约8000条记录,table本身分别有100K和300K的总记录。这些 table 中的所有字段都是 ints 和小的 varchars 除了第二个 table 中的一个字段(datos_progreso 在下面的代码中看到),它是一个文本字段并包含序列化数据,但没什么大不了的。

问题是,如果我 运行 完整 16000 条记录的脚本,我会得到一个 内部服务器错误 (在日志中没有任何解释),如果我 运行 1000 条记录的脚本一切正常,所以这似乎指出这是一个资源问题。

我试过(除其他外,我将在最后解释)将 memory_limit 从 128M 增加到 8GB(是的,你没有看错),max_execution_time 从 90 秒增加到 300 秒, max_input_vars 从 1000 到 10000,这并不能解决问题。

我的想法是,数据量并没有那么大,不会导致资源 运行 耗尽,但我已经尝试以多种方式优化脚本,但无法使其正常工作。我让它工作的唯一一次是 运行 像我上面提到的那样在一小部分记录上使用它。

我想知道是否可以通过脚本或 php-配置来解决此问题。顺便说一句,我无法用信息更改数据库tables。

代码

这只是我认为重要的相关代码位,脚本更长:

       $this->Usuario->bindModel(
            array('hasMany' => array(
                'UsuarioProgreso' => array('className' => 'UsuarioProgreso', 'foreignKey' => 'id_usuario', 'conditions' => array('UsuarioProgreso.id_campania' => $id_campania)))
                ));
    
       $usuarios = $this->Usuario->find('all', array(
            'conditions'=>array('Usuario.id_campania'=>$id_campania, 'Usuario.fecha_registro >'=>'2020-05-28'),
            'fields'=>array('Usuario.id_usuario', 'Usuario.login', 'Usuario.nombre', 'Usuario.apellido', 'Usuario.provincia', 'Usuario.telefono', 'Usuario.codigo_promocion'),
            'order'=>array('Usuario.login ASC')
        ));

        $usuario = null;
        $progreso_usuario = null;
        $datos_progreso = null;
        $i = 2;

        
        foreach ($usuarios as $usuario) {        
    
                if (isset($usuario['UsuarioProgreso']['datos_progreso'])) {
    
                    $datos_progreso = unserialize($progreso['UsuarioProgreso']['datos_progreso']);
    
                    $unit = 1;
                    $column = 'G';
    
                    while ($unit <= 60) {
    
                        if (isset($datos_progreso[$unit]['punt']))
                            $puntuacion = $datos_progreso[$unit]['punt'];
                        else
                            $puntuacion = ' ';
                        
                        $objSheet->getCell($column.$i)->setValue($puntuacion);
    
                        $column++;
                        $unit++;
                    }
    
                    $nivel = 1;
                    $unidad_nivel = array(1 => 64, 2 => 68, 3 => 72, 4 => 76, 5 => 80, 6 => 84);
    
                    while ($nivel <= 6) {
    
                        $unidad = $unidad_nivel[$nivel]; 
    
                        if (isset($datos_progreso[$unidad]['punt']))
                            $puntuacion = $datos_progreso[$unidad]['punt'];
                        else
                            $puntuacion = ' ';
                        
                        $objSheet->getCell($column.$i)->setValue($puntuacion);
    
                        $column++;
                        $nivel++;
                    }
                }
    
                //Free the variables
                $usuario = null;
                $progreso_usuario = null;
                $datos_progreso = null;

            $i++;
        }     

我试过的

我试过不使用 bindModel,而是分别加载两个 table 的信息。所以首先加载用户的所有信息,循环遍历它,并在每个循环中从 Table 2.

中获取该特定用户的信息

我也尝试过与上述类似的方法,但不是一次为来自 Table 1 的用户加载所有信息,而是先加载他们所有的 ID,然后循环遍历这些 ID 以获取来自 Table 1 和 Table 2 的信息。我想这样我会使用更少的内存。

我也试过不使用 CakePHP 的 find(),而是使用 fetchAll()"manual" 查询,因为经过一些研究,它似乎在内存方面更有效(似乎没有什么不同)


如果我可以提供任何其他信息以帮助更好地理解正在发生的事情,请告诉我:)


编辑:

按照评论中的建议,我已经在 shell 脚本中实现了它并且工作正常(需要一段时间,但没有问题完成)。

话虽如此,我仍然希望通过 Web 界面完成这项工作。为了弄清楚发生了什么,并且由于 error_logs 并没有真正显示任何相关内容,我决定自己进行一些性能测试。

测试之后,这些是我的发现:

还有什么其他设置可以限制 this/running 在 shell 脚本时不会失败?

我解决这个问题的方法是按照评论中的建议使用 shell 脚本。我明白我最初打算的方法是不正确的,虽然我无法弄清楚到底是什么导致了错误,但很明显使用网络脚本是问题的根源。