MySQL导出CSV邮件附件脚本添加text字段后挂起问题咨询
问题分析与解决方案
是的,note_text这个TEXT类型字段就是导致脚本挂起的核心原因,结合6万条数据的场景,具体问题出在这几点:
核心问题
- 内存过载:TEXT字段可存储最大64KB(或更大,取决于MySQL配置)的内容,6万条数据一次性全部读取到PHP数组
$data中,会瞬间占用大量内存,很容易超出PHP默认的内存限制,导致脚本卡死或无响应。 - 处理效率低下:将所有数据存入数组后,还要在内存中生成完整CSV字符串,再进行base64编码(编码后数据体积会膨胀约33%),这个过程会进一步加剧内存消耗,让进程长时间处于资源占用状态,最终挂起。
解决方案
1. 改用逐行处理,避免一次性加载所有数据
不要把所有数据都读到内存数组里,而是边查询数据库边写入CSV文件,大幅降低内存占用。
2. 生成物理CSV文件,而非内存字符串
直接将数据写入临时CSV文件,再读取文件内容处理邮件附件,避免内存中存储超大字符串。
3. 优化邮件发送逻辑(可选)
用专业邮件库(如PHPMailer)替代原生mail()函数,处理大附件更稳定,也无需手动拼接复杂的多部分邮件结构。
修改后的完整代码
<?php $mysqli = new mysqli("localhost", "*****", "*****", "****"); if (mysqli_connect_errno()) { printf("Connect failed: %s\n", mysqli_connect_error()); exit(); } // 创建临时CSV文件,用于存储导出数据 $csvFile = tempnam(sys_get_temp_dir(), 'notes_backup'); $fp = fopen($csvFile, 'w'); if (!$fp) { die("无法创建临时CSV文件"); } // 写入CSV表头 fputcsv($fp, array('ID', 'job_ID', 'note_date', 'note_author', 'note_text', 'flagged', 'FAO', 'time', 'category')); // 使用预处理语句逐行读取数据库数据,避免一次性加载所有记录 $query = "SELECT ID, job_ID, note_date, note_author, note_text, flagged, FAO, time, category FROM funeral_job_notes"; if ($stmt = $mysqli->prepare($query)) { $stmt->execute(); // 绑定结果到变量 $stmt->bind_result($id, $jobId, $noteDate, $noteAuthor, $noteText, $flagged, $fao, $time, $category); // 逐行读取并写入CSV while ($stmt->fetch()) { fputcsv($fp, array($id, $jobId, $noteDate, $noteAuthor, $noteText, $flagged, $fao, $time, $category)); } $stmt->close(); } fclose($fp); $mysqli->close(); function send_csv_mail($csvFilePath, $body, $to = 'me@me.com', $from = 'server@me.com', $subject = 'notes backup') { $multipartSep = '-----'.md5(time()).'-----'; // 读取CSV文件内容并进行base64编码 $csvContent = file_get_contents($csvFilePath); $attachment = chunk_split(base64_encode($csvContent)); $headers = array( "From: $from", "Reply-To: $from", "Content-Type: multipart/mixed; boundary=\"$multipartSep\"" ); // 拼接邮件内容 $bodyContent = "--$multipartSep\r\n" . "Content-Type: text/plain; charset=ISO-8859-1; format=flowed\r\n" . "Content-Transfer-Encoding: 7bit\r\n" . "\r\n" . "$body\r\n" . "--$multipartSep\r\n" . "Content-Type: text/csv; name=\"AFILE.csv\"\r\n" . "Content-Transfer-Encoding: base64\r\n" . "Content-Disposition: attachment; filename=\"AFILE.csv\"\r\n" . "\r\n" . "$attachment\r\n" . "--$multipartSep--"; $sendResult = mail($to, $subject, $bodyContent, implode("\r\n", $headers)); // 发送完成后删除临时文件 unlink($csvFilePath); return $sendResult; } // 执行邮件发送 echo "emaling now <br>"; $resp = send_csv_mail($csvFile, "job notes backup"); if( $resp ){ echo "Mail sent <br>" ; } else { echo "Mail not sent <br>"; } echo "all done <br>"; ?>
额外优化建议
- 临时调整PHP配置:如果仍遇到超时或内存不足,在脚本开头添加:
ini_set('memory_limit', '256M'); // 根据实际情况调整,比如512M set_time_limit(300); // 设置最长执行时间,单位秒 - 改用PHPMailer:原生
mail()函数处理大附件稳定性较差,PHPMailer支持SMTP协议,还能直接添加文件附件,无需手动处理base64编码,大幅降低出错概率。 - 分批次导出:若单份CSV体积过大,可分多次查询(比如每次查1万条),生成多个附件分开发送,或压缩CSV后再发送。
内容的提问来源于stack exchange,提问作者Mark hardy
相关产品推荐
相关产品推荐

