资讯动态

十万条数据导出实战:避免内存爆掉的终极技巧

发布时间:2026/10/24 5:40:54 来源:尧图企业网站定制
上周帮同事处理一个后台导出需求数据量大概十万条出头一开始用最土的办法拼 CSV结果运营那边打开就乱码还丢了几列。后来换成 PhpSpreadsheet折腾了一下午总算跑通顺手把踩过的坑记下来给同样要做导出的朋友省点时间。一、先装依赖别急着写代码PhpSpreadsheet 是 PHPExcel 的官方继任者PHPExcel 早就停止维护了新项目别再用了。用 Composer 装一下就行composer require phpoffice/phpspreadsheet注意版本和 PHP 版本要对得上。PhpSpreadsheet 2.x 要求 PHP 8.1 以上如果你的项目还跑在 PHP 7.4那就锁 1.x 的版本不然装完直接报语法错误。另外记得确认ext-gd、ext-zip、ext-xml这几个扩展是开着的导出 xlsx 会用到 zip。二、最基础的导出写法先看一个能跑的最小例子把数组写进单元格再输出?php require vendor/autoload.php; use PhpOffice\PhpSpreadsheet\Spreadsheet; use PhpOffice\PhpSpreadsheet\Writer\Xlsx; $spreadsheet new Spreadsheet(); $sheet $spreadsheet-getActiveSheet(); // 表头 $sheet-fromArray([ID, 用户名, 邮箱, 注册时间], null, A1); // 数据 $rows [ [1, 张三, zhangsanexample.com, 2024-01-01], [2, 李四, lisiexample.com, 2024-01-02], ]; $sheet-fromArray($rows, null, A2); $writer new Xlsx($spreadsheet); $writer-save(demo.xlsx);这段代码跑起来没问题但如果你直接把它套到十万条数据上大概率会遇到两个情况一是内存爆掉二是浏览器一直转圈最后超时。三、十万条数据的关键分批写入 关掉缓存PhpSpreadsheet 默认会把每个单元格对象都放在内存里十万行乘以十几列内存占用轻松上 G。解决办法其实不复杂核心就两点用fromArray一次性写一批而不是一个单元格一个单元格地setCellValue写之前把单元格缓存关掉减少对象开销。?php require vendor/autoload.php; use PhpOffice\PhpSpreadsheet\Spreadsheet; use PhpOffice\PhpSpreadsheet\Writer\Xlsx; use PhpOffice\PhpSpreadsheet\Cell\DataType; // 关掉单元格缓存降低内存 \PhpOffice\PhpSpreadsheet\Settings::setCache(null); $spreadsheet new Spreadsheet(); $sheet $spreadsheet-getActiveSheet(); $sheet-fromArray([ID, 用户名, 邮箱, 注册时间], null, A1); $pdo new PDO(mysql:host127.0.0.1;dbnamedemo;charsetutf8mb4, root, 123456); $pdo-setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); $pageSize 2000; $page 0; $rowIndex 2; while (true) { $offset $page * $pageSize; $stmt $pdo-prepare(SELECT id, name, email, created_at FROM users LIMIT :offset, :size); $stmt-bindValue(:offset, $offset, PDO::PARAM_INT); $stmt-bindValue(:size, $pageSize, PDO::PARAM_INT); $stmt-execute(); $list $stmt-fetchAll(PDO::FETCH_NUM); if (empty($list)) { break; } $sheet-fromArray($list, null, A . $rowIndex); $rowIndex count($list); $page; // 及时释放 unset($list, $stmt); } $writer new Xlsx($spreadsheet); $writer-save(users.xlsx);这里用分页查询而不是fetchAll一次性捞全表是很关键的一步。十万条数据全塞进 PHP 数组光数组本身就要占不少内存分页之后峰值能压下来一大截。四、几个容易忽略的细节1. 长数字会变成科学计数法。像订单号、身份证号这种Excel 会自动识别成数字然后显示成 1.23E17。写入前把单元格格式设成文本$sheet-setCellValueExplicit(A2, 202401011234567890, DataType::TYPE_STRING);用fromArray批量写的时候可以先把这一列整列设成文本格式再写数据。2. 中文乱码。xlsx 本身是 UTF-8一般不会乱码乱码多半是数据库连接没设charsetutf8mb4或者导出的是 CSV 而不是 xlsx。CSV 的话记得加 BOM。3. 执行时间。十万条数据导出通常要几十秒记得在脚本开头加set_time_limit(0)不然跑到一半被 PHP 掐掉。如果是 Web 请求触发的最好改成异步任务前端轮询下载地址别让用户干等着。4. 内存上限。可以在脚本里临时调一下ini_set(memory_limit, 512M)但更推荐的做法还是控制单次写入的数据量别指望无限加内存。五、数据量再大怎么办如果哪天数据涨到五十万、一百万PhpSpreadsheet 就有点吃力了。这时候可以考虑两个方向一是用Xlsx的流式写入边生成边往临时文件里写二是干脆换 OpenSpout 这类专门为大数据量设计的库内存占用会小很多。不过对大多数后台导出场景来说十万条用 PhpSpreadsheet 完全够用把分批和缓存这两点做好就行。最后提醒一句导出功能上线前一定要拿真实数据量压一遍别等运营点下载的时候才发现超时那就尴尬了。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑