PHP7.0坑之SIGSEGV

来由

项目需求需要做一个批量数据导出程序,数据量在百万级,解决方案为每次10000条分页取出数据,导出到缓冲区实现持续导出csv文件。部分代码如下。

    public function exportBatCsv($pager, $fileName,$getDataM,$page_name,$diyparameters)
    {
        // 设置响应头
        header("Content-Type: application/CSV");
        header(
            "Content-Disposition: attachment; filename=" . $fileName . ".csv"
        );
        header("Expires: 0");

        $nowPage = 1;
        $limit = isset($pager['exportPageLimit']) ? $pager['exportPageLimit'] : 5000;

        $pager['pageSize'] = $limit;
        $recordCount = $pager['recordCount'];
        $totalPager = ceil($recordCount/$limit);
        $pager['pageCount'] = $totalPager;
        $fp = fopen('php://output', 'a');  
        $exportFunc = $pager['exportAllFunc'];
        // 判断一下表头数组是否有数据
        if ($pager["exportColumns"] != null
            && count($pager["exportColumns"]) > 0
        ) {
            // 循环写入表头
            foreach ($pager["exportColumns"] as $key => $column) {
                $head[$key] = iconv("UTF-8", "GBK",$column["title"] );
            }
            fputcsv($fp, $head);
            // 判断表中是否有数据
            if ($recordCount > 0) {
                for ($nowPage=1; $nowPage <= $totalPager; $nowPage++) { 
                    $pager['nowPage'] = $nowPage;
                    $_POST['dtGridPager'] = json_encode($pager);
                    $dataArr = $getDataM->$exportFunc($pager['diyparameters'],1); 
                    // 循环写入表中数据
                    foreach ($dataArr['datas'] as $record) {
                        $rs = array();
                        foreach ($pager["exportColumns"] as $tkey => $column) {
                            $content = $record[$column["id"]];
                            // 如果内容未被处理则进行格式化 
                            if (!$pager["exportDataIsProcessed"]) {
                                $content = $this::formatContent(
                                    $column, $content
                                );
                            }
                            $rs[$tkey] = iconv("UTF-8", "GBK//TRANSLIT//IGNORE",$content );
                        }
                        fputcsv($fp, $rs);
                        unset($rs);
                    }
                    ob_flush();         //释放内存
                    flush();
                }
            }
        }
    }

编写完成后,测试环境测试,没问题,上线!然后坑就开始了!!!!!

 

线上服务器环境:centos6.9+nginx1.14.1+php7.0.32,数据库为线上库的一个直供查询的从库,且没有设置超时。

上线后,执行导出,导出过程中导出部分数据后chrome浏览器即报错'下载失败,网络错误'。开始解决问题

  1. 首先查看程序日志,程序日志没找到有用错误信息。
  2. 对上面程序打断点,屏蔽掉fputcsv后,再执行导出报错502。502?难道是超时问题?
  3. 随机检查nginx和php相关配置,包括 fastcgi_connect_timeout,fastcgi_send_timeout,fastcgi_read_timeout,request_terminate_timeout 以及php.ini中max_execution_time,均已配置为不超时或者超时时间很大,排除超时问题。
  4. 查看nginx服务日志,发现报错信息recv() failed (104: Connection reset by peer) while reading response header from upstream。意思大概是nginx在接受php返回数据时链接 被重置导致无响应中断。WTF?这是什么鬼?好,有问题百度,然后大部分答案都在说是超时问题,然而超时问题已经在上面检查过,绝无可能。
  5. 继续查看php-fpm日志,发现php-fpm报错  exited on signal 11 (SIGSEGV)  内核抛出中断信号????WTF,这又是什么鬼?然后发现官方解释为 

               SIGSEGV --- Segment Fault. The possible cases of your encountering this error are:

               1.buffer overflow --- usually caused by a pointer reference out of range.
               2.stack overflow --- please keep in mind that the default stack size is 8192K.
               3.illegal file access --- file operations are forbidden on our judge system. 

           意思是无非有三种可能,缓冲区溢出,堆溢出,内存溢出或者操作不合法内存。还是检查程序,因为数据量大,在查询以及输出方面             都有注意释放内存。并且php这种语言几乎不直接操作内存,如果有堆溢出,那问题可能就不在程序或者nginx本身,而在php。旋               即将同样的程序部署到了一套nginx和centos版本相同,配置相同,但是php版本为php7.1的服务器上,执行导出,正常无报                       错!!!!至此问题解决。

总结

         在较大数据量操作下,PHP7.0版本应该存在一些堆栈或者操作系统缓冲区上面的bug,会导致一些溢出问题。然而我再PHP的更新日志中并没有找到相关问题描述。只是将版本升级到7.1该问题就不复存在。具体哪里出问题可以再用core去追溯核心问题。如果有碰到此类问题的朋友仅供参考

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值