数百万条记录的CSV输出永远需要时间-PHP-Mysql

数百万条记录的CSV输出永远需要时间-PHP-Mysql,php,mysql,csv,Php,Mysql,Csv,我已经编写了下面的PHP代码,如果要比较的记录数较少,如果数据库有2000000条记录要比较(一个表),它就不会下载CSV文件,而且会花费很长时间。不确定我的查询是错误的还是PHP中的一些问题,比较之后应该立即开始下载文件 Mysql进程列表显示以下内容 PHP代码 $ids="1,23,24"; header('Content-Type: text/csv; charset=utf-8'); header('Content-Disposition: a

我已经编写了下面的PHP代码,如果要比较的记录数较少,如果数据库有2000000条记录要比较(一个表),它就不会下载CSV文件,而且会花费很长时间。不确定我的查询是错误的还是PHP中的一些问题,比较之后应该立即开始下载文件

Mysql进程列表显示以下内容

PHP代码

    $ids="1,23,24";
        header('Content-Type: text/csv; charset=utf-8');
        header('Content-Disposition: attachment; filename=Output.csv');

        // create a file pointer connected to the output stream
        $output = fopen('php://output', 'w');   
        //output the column headings
        fputcsv($output, array('code', 'Min', 'IMin','ICare','IRCare'));

             $sql1 = 'select d.code, d1.inter_Rate, d2.intra_rate, d1.carrier as c1, d2.carrier as c2
                from S_DATA d
                left join S_DATA d1 ON d1.code = d.code 
                and d1.inter_Rate = (select min(s1.inter_Rate) from S_DATA s1 where s1.code = d1.code)
                left join S_DATA d2 ON d2.code = d.code 
                and d2.intra_rate = (select min(s2.intra_rate) from S_DATA s2 where s2.code = d2.code)
                where d.file_log in ('.$ids.')
                group by d.code';

              $rp =  mysqli_query($con,$sql1,MYSQLI_USE_RESULT);   
               $total = mysqli_num_rows($rp);
                    if($total != 0) {
                                 while($row = mysqli_fetch_assoc($rp))
                                    {
                                    fputcsv($output, $row);
                                    }
                                 }  
         mysqli_free_result($rp);
         exit;
     ?>
S_数据结构

以下索引

下面解释一下


我建议您查看两个php.ini参数:

  • 内存限制-设置分配给脚本的内存量
  • max_execution_time-设置脚本在终止之前必须运行多长时间

通过增加其中一个(或两个)值,您的php脚本应该运行。

至少有两个因素会导致响应缓慢

  • 表中使用的索引或缺少索引。这会影响实际执行查询所需的时间
  • 事实上,您正在将每一行写入磁盘
  • 第一个问题我们需要更多的信息来帮助你,正如刚才有人评论的那样。在select查询前面使用
    EXPLAIN
    ,并将其添加到问题中。由于直接运行查询只需几毫秒,因此这暗示PHP代码是实际的瓶颈。只要PHP代码还没有完成抓取,它就会继续发送数据

    第二部分更容易立即修复。也就是说,通过使用一个临时变量,在每次迭代中添加它,而不是写入磁盘。但是,为了避免在服务器上使用过多的RAM,还需要检查所述变量的大小。如果它达到您指定的限制(例如2MB),则可以将其写入磁盘并清空变量

    大概是这样的:

    $tmp = '';
    while ($data = $res->get ()) {
        // Note that you need to write the format_csv () function yourself.
        $tmp .= format_csv ($data);
    
        // If the size is above an arbitrary, but sensible, limit.
        if (strlen ($tmp) >= 2000000) {
            // The CSV is pre-formatted, thus file_put_contents ()
            file_put_contents ($file, $tmp);
            $tmp = '';
        }
    }
    
    请注意,
    fputcsv()
    一次只处理一行,因此您需要编写一个使用替代流(或其他技巧)的包装函数。因此,您可以从
    fputscsv()
    以字符串形式返回结果,或者(不推荐)编写自己的CSV生成代码


    不过,您确实应该首先通过分析器来检查代码,找出代码的哪些部分是真正的瓶颈。

    @ChristianF提出了一个很好的观点,可能只是查询速度慢而已

    但是我认为让你慢下来的是PHP的mysqli中的一个怪癖,默认情况下,它会等待整个查询完成,然后从mysqli_query()函数返回,然后完全下载到PHP的内存中

    对于非常大的查询,这显然是有问题的。您已经将查询结果流式传输到输出,这是一件好事,但您的mysql驱动程序没有这样做

    如果你看

    还有MYSQLI_USE_RESULT选项,它允许您在结果行从Mysql进入PHP时使用它们。这里有一个主要的警告,在开始另一个查询之前,必须使用mysqli_free_result(),否则会出现错误。mysqli_num_rows()也不会处理流式处理结果,因为流开头的行总数可能未知

    $rp =  mysqli_query($con, $sql1, MYSQLI_USE_RESULT);  
      while($row = mysqli_fetch_assoc($rp)) {
        fputcsv($output, $row);
      }
    }
    mysqli_free_result($rp);
    

    那么,您的表上有哪些索引?您是否对查询进行了解释以显示数据库是如何执行查询的?您是否测试了执行查询需要多长时间?如果这不是瓶颈,你可以试着分批写…@MarkBaker我用屏幕更新了问题indexes@hummingBird几乎需要显示第0-24行(总共155673行,查询需要0.0369秒。)@memory_limit 128M,max_execution_time 30问题不在于脚本没有运行,但是它所花费的时间对于它应该完成的任务来说是过度的。换句话说,OP希望它走得更快。你的答案没有这样做。谢谢,我也用更具体的信息更新了我的答案。我使用了MYSQLI_USE_RESULT,但结果没有任何差别,我已经更新了问题